Partially defaulting template arguments using typedefs? - c++

I am trying to do something like this:
template <typename T,bool Strong=true>
class Pointer {...};
template <typename T>
typedef Pointer<T,false> WeakPointer;
But this is a compile error ("a typedef template is illegal" VC).
I am trying to avoid doing this using inheritance, beacuse that's more unnecessary work (rewriting constructors, operator =, back-and-forth casting, friendship...).
Any ideas?

C++0x will alleviate this issue, but as it stands you cannot.
The common work-around is this:
template <typename T,bool Strong=true>
class Pointer {...};
template <typename T>
struct WeakPointer
{
typedef Pointer<T,false> value_type;
};
So instead of:
typedef WeakPointer<int> WeakInt;
You get:
typedef WeakPointer<int>::value_type WeakInt;

C++03 doesn't support templated typedefs. You'd have to specify both types:
typedef Pointer<int,false> WeakIntPointer;
I know that isn't very helpful, but it's a reality of the language. Luckily, C++0x will support template typedefs.
For now, you'd really have to make a templated WeakPointer class which takes a template parameter to indicate the type.

Related

How to get at a C++ Container<T>'s T if no Container::value_type is provided?

It's rather common that container templates contain a value_type typedef. This makes it easy to create other templated code, most recently concepts, which are able to extract the T if only having been given Container and not Container<T>.
However, not all containers (or other templated classes) define such a value_type, especially older ones.
Is it possible to get to the contained T even without it?
I know there are tricks like "if it is an iterator then .begin() should return a value of that type", but that does not help us to e.g. write a concept-requirement that checks whether a class's .begin() indeed follows the requirements that iterators have.
Here's a solution that's similar to Quimby's solution but using partial template specialization instead:
template<typename...>
struct inner_type_impl;
template<template<typename...> typename C, typename T, typename...Args>
struct inner_type_impl<C<T,Args...>>
{
using type = T;
};
template<typename T>
using inner_type = typename inner_type_impl<T>::type;
Here's a demo that's borrowed from Quimby's solution.
Class template specialization or template argument deduction can be used to implement this. Something like the following should work as long as the inner type is the first template argument:
#include <type_traits>
// Consider the first template argument to be the inner type.
template<template<typename,typename...>class C,typename T,typename...Args>
auto inner_type_impl(const C<T,Args...>* v)->T*{return nullptr;};
template<typename T>
using inner_type = std::remove_pointer_t<decltype(inner_type_impl((T*)nullptr))>;
template<typename T>
struct Container;
// Will still deduce T
template<typename T,typename...Extras>
struct ContainerExtraParams;
static_assert(std::is_same_v<int,inner_type<Container<int>>>);
static_assert(std::is_same_v<int,inner_type<ContainerExtraParams<int,double,float>>>);
I use pointers to make the code valid in evaluated contexts too. Contrary to a possible solution involving std::declval.

Prevent ADL on template parameters

I currently have a class template that takes a series of types. Each type may need to be instantiated with the class, itself. What I currently have is something like this:
template <typename... Types>
struct TypeList; // Not defined
struct Placeholder; // Not defined
template <typename Types, typename AnotherType = Default>
class MyClass
{
// ...
};
You can then use it like this:
typedef MyClass<TypeList<Container1<Placeholder>, Container2<std::string,
Placeholder>, OtherType>, OptionalType> MyTypedef;
MyTypedef my_object;
MyClass will replace appearances of Placeholder with itself, use the resulting types, and all is well.
The problem occurs when I try to do something like either of these:
MyTypedef *my_ptr = &my_object;
my_free_function(my_object);
Both of these cause a compiler error, because the compiler tries to instantiate Container1<Placeholder> and Container2<std::string, Placeholder> to do argument dependent lookup (ADL), and this instantiation with Placeholder, itself, fails.
I know it is possible to avoid ADL by doing, e.g.,
MyTypedef *my_ptr = std::addressof(my_object);
(my_free_function)(my_object);
However, I don't want to burden the user of MyClass with having to constantly suppress ADL. Is there another, straightforward way to have the user provide a list of types without those types being used for ADL?
Okay, I got everything working. The trick was to use a dependent type instead of using a template, directly. My final solution was to define TypeList as follows:
template <typename... Types>
struct TypeList
{
private:
struct Holder
{
private:
typedef TypeList<Types...> InnerTypeList;
template <typename Types, typename AnotherType>
friend class MyClass;
};
public:
typedef Holder type;
};
Then, the users of MyClass can do
typedef MyClass<TypeList<Container1<Placeholder>, Container2<std::string,
Placeholder>::type, OtherType>, OptionalType> MyTypedef;
MyTypedef my_object;
Note the addition of '::type'
Finally, in MyClass, I replaced
typedef typename SubstituteType<Types, Placeholder, MyClass>::type InternalTypeList;
with
typedef typename SubstituteType<Types::InnerTypeList, Placeholder, MyClass>::type
InternalTypeList;
giving me the same type for InternalTypeList as before.
Because the dependent type Holder has no template parameters of it's own, the compiler doesn't have to instantiate the Placeholder types for ADL purposes, and everything works properly.

templated recursive data types

I have a recursive data type like this:
template<typename T>
struct SomeType {
std::map<T, SomeType<T>> mapping;
};
SomeType<int> foo;
This works fine, but replacing std::map with std::unordered_map results in a compile error due to an incomplete type. Am I (or gcc) making an error somewhere? or is this just part of the standard?
I would also like to have the internal container determined by a template parameter (like std::stack and std::queue), but I can't figure out a way to do it since that would require SomeType to already be defined.
Incomplete example:
template<typename T, typename C = std::map<T, SomeType<[???]>>>
struct SomeType {
C mapping;
};
SomeType<int, [???]> foo;
I know this can be done with runtime indirection, but that's not what I'm looking for.
Your class is incomplete anywhere before the final } of its definition. So the mapping member is using incomplete type SomeType in its type's template arguments.
The standard does not allow this, and it is pure luck that it works with some STL containers.
Your second questions falls under the same answer -- it is illegal to do that in the first place.
You cannot define a template with recursive default parameters for obvious reasons. You also cannot instantiate standard library container templates on incomplete types, because the standard says so (otherwise it's undefined behaviour). The usual PIMPL idiom may help, though:
#include <map>
#include <memory>
template <typename T> class SomeType
{
typedef std::map<T, SomeType<T>> map_type;
typedef std::unique_ptr<map_type> map_ptr;
map_ptr pimpl;
public:
SomeType() : pimpl(new map_type) { }
};
While you cannot use incomplete types with containers, you can do it with smart pointers.
And while you cannot create template types with undefined types parameters, you can use some tricks here:
template<typename T, template <typename U, typename V, typename... Args> class Container = std::unordered_map >
struct SomeType {
Container<T, std::unique_ptr<SomeType> > mapping;
};

How to intentionally cause a compile-time error on template instantiation

Sometimes when coding with C++ templates, you want to prevent users from instantiating a specific specialization or set of specializations, because the result would be nonsensical. So you can define a (specific or partial) specialization whose definition, if instantiated, would cause a compiler error. The goal would be, if a user "misuses" the template, to cause a compiler error right next to a comment in your header file explaining what not to do, rather than leaving the compiler to come up with some confusing error message by its own devices, or maybe allowing the questionable code to compile.
Example:
template <typename T> struct MyClassTemplate {
// ...
};
template <typename T> struct MyClassTemplate<T*> {
// Do not use MyClassTemplate with a pointer type!
typedef typename T::intentional_error err;
};
There are a number of ways to do this (depending on whether your specialization is a complete or partial specialization of a class or function). But the syntax used must (?) depend on a template parameter, or else the compiler will complain when it first parses the intentional-error definition. The example above has a hole in that somebody could stubbornly define an intentional_error nested type or member typedef (though I'd say they would then deserve whatever problems come up as a result). But if you use a trick too fancy, you're likely to get an indecipherable and/or misleading compiler error message, which mostly defeats the purpose.
Are there better straightforward ways to disallow template instantiations?
I'm aware that in C++0x, template Concepts and deleted function declarations will provide much better control over this sort of thing, but I'm looking for answers that are valid C++03.
You could just omit defining it.
template <typename T> struct MyClassTemplate<T*>;
You could also derive from a non-defined specialization
template <typename T> struct invalid;
template <typename T> struct MyClassTemplate<T*> : invalid<T> { };
Note that explicit specializations that declare classes or functions will never depend on template parameters. So, stuff like this that depend on template parameters can't work anyway. In that case, declaring a non-defined explicit specialization should be sufficient
template<> struct MyClassTemplate<int*>;
For me this sounds like a typical case for static_assert from C++0x or BOOST_STATIC_ASSERT. The static_assert functionality has the advantage that you can pass a custom error message so that the reason for the error is more clear.
Both ways are giving you the opportunity to prematurely end the compilation process under some custom defined compile time condition.
with static_assert:
template <typename T> struct MyClassTemplate<T*> {
static_assert(always_false<T>::value, "Do not use MyClassTemplate with a pointer type!");
};
with BOOST_STATIC_ASSERT
template <typename T> struct MyClassTemplate<T*> {
// Do not use MyClassTemplate with a pointer type!
BOOST_STATIC_ASSERT(always_false<T>::value);
};
Always false would look something like this:
template< typename T >
struct always_false {
enum { value = false };
};
HTH
Edit: Fixed the examples to make them actually work ;-) Thanks to GMan!
If you don't want to use a library, this construct is pretty reliable (it's roughly what Boost does internally):
template <typename T>
void must_be_specialized(T const&)
{
enum dummy { d = (sizeof(struct must_be_specialized_for_this_type)
== sizeof(T)) };
}
You can put something analogous in a specialization to disallow instantiation of the template with that type. I wouldn't, personally, worry about must_be_specialized_for_this_type gaining a definition from somewhere, but you could use a forward declaration to squirrel it away in a private namespace if you really wanted.
Concepts were removed from '0x. You can use a library, like Boost Concept Check.
"Are there better straightforward ways to disallow template instantiations?" Nothing significantly better than what you have already identified. I am pretty sure C++ protection mechanisms are there to protect you from accident not from malice. And someone defining a specialisation or a class to break your intended use I would consider malicious. Perhaps you could hit the person in the back of the head each time they do it.
I personally prefer to put the checks into templates that exist only to describe the checks. That allows interesting combinations of inheritance and templates.
template <class T>
class not_with_pointer_t { };
template <class T>
class not_with_pointer_t<T*>;
template <class T>
class some_class_t : public not_with_pointer_t<T> { };
template <class T, template <class U> class base_t>
class another_class_t : public base_t<T> { };
typedef some_class_t<int> a_t; // ok
typedef some_class_t<void*> b_t; // error if instantiated
typedef another_class_t<void*, not_with_pointer_t> c_t; // error if instantiated
template <class T> class unrestricted_t { };
typedef another_class_t<void*, unrestricted_t> d_t; // ok
boost::enable_if

Alias for a C++ template?

typedef boost::interprocess::managed_shared_memory::segment_manager
segment_manager_t; // Works fine, segment_manager is a class
typedef boost::interprocess::adaptive_pool
allocator_t; // Can't do this, adaptive_pool is a template
The idea is that if I want to switch between boost interprocess' several different options for shared memory and allocators, I just modify the typedefs. Unfortunately the allocators are templates, so I can't typedef the allocator I want to use.
Is there a way to achieve an alias to a template in C++? (Except for the obvious #define ALLOCATOR_T boost::interprocess::adaptive_pool)
Yes, (if I understand your question correctly) you can "wrap" the template into a struct like:
template<typename T>
class SomeClass;
template<typename T>
struct MyTypeDef
{
typedef SomeClass<T> type;
};
and use it as:
MyTypeDef<T>::type
Edit: C++0x would support something like
template<typename T>
using MyType = SomeClass<T>;
Edit2: In case of your example
typedef boost::interprocess::adaptive_pool allocator_t;
can be
template<typename T>
struct allocator_t
{
typedef boost::interprocess::adaptive_pool<T> type;
}
and used as
allocator_t<SomeClass>::type
C++ doesn't support this, though it is slated to be fixed in the new standard. You might get away with deriving a new class template from adaptive_pool if there are no non-trivial constructors (or if you're happy to write a few forwarding constructors).
template <class T>
class allocator_t : public adaptive_pool<T> {
public:
// Just making up a forwarding constructor as an example. I know nothing
// anything about adaptive_pool.
allocator_t(int arg1, float arg2) : adaptive_pool<T>(arg1, arg2) { }
};
EDIT: Forget this answer. My vote goes to #Akanksh.