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.
Related
I have a class that looks something like this:
template<class KeyType, class... Types>
class BasicCompound
{
public:
using mapped_type = std::variant
<
ValueWrapper<BasicCompound>
, ValueWrapper<Types>...
>;
using key_type = KeyType;
// Accessors for retreiving and modifying content
// ...
private:
std::map<key_type, mapped_type> m_content;
};
ValueWrapper decides to put the content either inline or in a std::unique_ptr. Would it be possible with a similar interface, possibly through some kind of proxy, to make the recursiveness optional? By optional I mean that the user should not automatically get the possibility to store the BasicCompound inside itself, but rather specify it in the list of types.
What I have thought of:
A using directive does not work. A new type cannot be defined in it self, and a predeclaration of a following typedef is not allowed.
Adding a bool to the list of types, and use std::conditional_t for mapped_type. However, if the user wants to store an X<BasicCompound>, this approach fails.
Inject mapped_type from outside. Then I cannot hide the use of the ValueWrapper thing.
Using inheritance over a typdef like
struct MyCompound : BasicCompound<std::string, MyCompound, int> {};
This works but then the structure is not strictly recursive, as MyCompound now is a different type from BasicCompound. Maybe a CRTP-like approach could solve that problem, but then the inner compound type must be treated differently than the other types.
You can't have use a specialization of a class template as one of the template arguments of that specialization; there's no way to write it, and its name would be infinitely long. You can, however, use a wrapper to hold the recursive type:
template<class> class MaybeRecursive;
template<class T>
struct Wrapper {
using type=T;
};
struct MRWrapper {
using type=MaybeRecursive<MRWrapper>;
};
template<class T>
struct MaybeRecursive {
using type=typename T::type;
type *child;
};
void f() {
int x;
MaybeRecursive<Wrapper<int>> nonrec={&x};
MRWrapper::type rec={&rec};
}
MRWrapper can be made a class template to provide additional template arguments to MaybeRecursive.
I would like to use an enum argument of a template, to restrict a second argument, a class, to in turn taking an member of the enum as an argument as it's templated parameter. In code, I would expect this to look like:
CObject<EObjectTag, CSubObject<EObjectTag::CAT_A>> cObject;
this should work, however:
CObject<EObjectTag, CSubObject<ENotAnObjectTag::CAT_OTHER>> cObject;
should fail as ENotAnObjectTag::CAT_OTHER is not a element of EObjectTag.
My implementation (attempt) of this, is as follows and bombs out during compilation (on gcc version 4.9.2 (Ubuntu 4.9.2-10ubuntu13)) with the error message:
source.cc:16:45: error: ‘SUBOBJECT_TAG’ was not declared in this scope
struct CObject>
#include <iostream>
#include <typeinfo>
enum class EObjectTag {CAT_A, CAT_B, CAT_OTHER};
// CSubObject
template<class OBJECT_TAG_T, OBJECT_TAG_T OBJECT_TAG>
struct CSubObject { OBJECT_TAG_T m_tTag = OBJECT_TAG; };
// CObject - Forward declaration
template <class SUBOBJECT_TAG_T, template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T>
struct CObject;
// CObject - Specialization
template <class SUBOBJECT_TAG_T, template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T>
struct CObject<SUBOBJECT_T<SUBOBJECT_TAG_T, SUBOBJECT_TAG>>
{
public:
SUBOBJECT_T<SUBOBJECT_TAG_T, SUBOBJECT_TAG> m_cSubObject;
};
int main() {
// The aim is that the second object only accepts a tag that
// belongs to EObjectTag
CObject<EObjectTag, CSubObject<EObjectTag::CAT_A>> cObject;
return 0;
}
The final use case for this involves replacing CSubObject with CObject so that we can use recursion to define a hierarchy of tagged objects, which also requires the use of variadic templates to have multiple objects at the same level. For example:
/* EBase, */
CObject</*EBase::BASE,*/ EObject,
CObject<EObject::INIT, EInitObject,
CObject<EInitObject::INIT_FOO>,
CObject<EInitObject::INIT_BAR>,
>,
CObject<EObject::COUNT, ECountObject,
CObject<ECountObject::COUNT_FOO>,
CObject<ECountObject::COUNT_BAR>,
>,
> cMyObjectHierarchy;
The commented out references to EBase (an enum internal to the library) are there to keep the template parameters of CObject consistent, I would plan (if possible) to do this automatically via template specialization or default arguments.
My goals of specifying this hierarchy of objects would in addition include:
Avoid forcing the user of this library to define additional classes
or structs in their program
Leverage compile time checking via the
templating of CObject with an enum, whose functions in turn use that
enum as an argument to a set of functions common to all CObjects
An argument of template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T is a template, not an instance of a template. CSubObject<blah> cannot match the kind template<...>class, because CSubObject<blah> is a type that is generated by the template, not a template. template<...>class parameters are templates, not types.
In addition, CSubObject is of kind template<class T, T> class, not template<SUBOBJECT_TAG_T>class. It takes two arguments, the first a type, the second a constant of that type: the kind template<SUBOBJECT_TAG_T>class is a template that takes one argument of type SUBOJECT_TAG_T. These are unrelated kinds of template.
Second, you seem to have issues with template specializations. Template specializations are pattern-matching of your primary specialization. They are not "new overloads". So arguments to CObject must first match the kinds of arguments the primary specialization of CObject expects. The stuff within template< blah > are used to pattern match the pattern in the CObject< blah > part of the specialization.
In general, it is conventional to use ALL CAPS only for macros, not for template arguments.
These are all problems with your code in your question. Your code lacks a clear problem statement or question, so the best I can do is describe fixes to your myriad of problems.
You have revised your question a bit.
template<class T, class U>
struct CObject;
template<class T, template<class Q, Q>class Z, T t>
struct CObject< T, Z<T, t> > {
};
live example.
Now, you still have to pass CSubObject<EObjectTag, EObjectTag::CAT_A> as the 2nd parameter.
You could also add a specialization:
template<class T, template<T>class Z, T t>
struct CObject< T, Z<t> > {
};
which, if you had a template<EObjectTag tag> struct Example;, you could CObject< EObjectTag, Example<EObjectTag::bob> > as well.
I made some changes to make it compile. Although I'm not 100% sure if this actually does what you want it to do; I agree with most of what Yakk sais in his answer.
Note: the following will not compile because I deliberately tried to mix a type of one enum with a value of another enum to verify that it indeed triggers a compile-time error, which I think is sort of what you asked for.
#include <iostream>
#include <typeinfo>
enum class EObjectTag {CAT_A, CAT_B, CAT_OTHER};
enum class FObjectTag {DOG_A, DOG_B, DOG_OTHER};
// CSubObject
template<typename OBJECT_TAG_T, OBJECT_TAG_T OBJECT_TAG>
struct CSubObject { OBJECT_TAG_T m_tTag = OBJECT_TAG; };
// CObject - Specialization
template <class SUBOBJECT_TAG_T, SUBOBJECT_TAG_T SUBOBJECT_TAG, template <typename TYPE_T, TYPE_T TYPE> class SUBOBJECT_T>
struct CObject
{
public:
SUBOBJECT_T<SUBOBJECT_TAG_T, SUBOBJECT_TAG> m_cSubObject;
};
int main() {
// The aim is that the second object only accepts a tag that
// belongs to EObjectTag
CObject<EObjectTag, EObjectTag::CAT_A, CSubObject> cObject1;
CObject<EObjectTag, FObjectTag::DOG_B, CSubObject> cObject2;
return 0;
}
I have learned that data structures can be created using templates in the following way:
template<typename T>
struct X {
T weight;
int age;
};
The functions can also use templates in the following way:
template <class T>
T func_name(int age, T human) {
something_here;
}
One of the difference s is that in the first case we use typename while in the second case we use class.
I found code that contains the following:
template<typename S, typename T>
bool is_null(const row<T>& r)
So, what I cannot understand is why we use typename (and not class) in combination with functions. Shouldn't we use class?
In this context, there is no technical difference between the keyword typename and the keyword class. It's just a matter of style. The meaning of your first two code examples would not change one bit if they started with template<class T> struct X and template <typename T> T func_name(int age, T human). (I tend to use class when I mean to imply the template parameter should be a class, and not something like int.)
When template was first introduced, it ONLY allowed the existing keyword class as an indicator "this is a template argument". Since this becomes rather daft when the template argument isn't actually a class (a function pointer, integer type, or some other "not a class" type), the typename was introduced to make it more clear that template<typename T> struct something { T x; }; allows something<int> a; as well as something<name_of_class> a;.
For all intents and purposes, class and typename in the case of template parameters is interchangeable, and it's just a matter of style which you choose to do [most people probably prefer if you stick to one, not mixing the two - or, perhaps use class when the type HAS TO be a class, and typename when it can be "any" type].
In the context of template parameter definitions the keywords typename and class are synonymous.
Just about everyone has a convention they tend to stick with. I personally prefer to always use class here and reserve the typename keyword for its other use.
The other use for typename is to disambiguate a dependent type in a template definition or declaration.
Here is an example from wikipedia:
template <typename T>
void foo(const T& t)
{
// declares a pointer to an object of type T::bar
T::bar * p;
}
struct StructWithBarAsType {
typedef int bar;
};
int main() {
StructWithBarAsType x;
foo(x);
}
If you look closely you will notice in the line T::bar * p;, bar is dependent on a template parameter T which is ambiguous to the compiler as bar can be either a type or a value depending on the context of the type T used for instantiating the template. The default is to treat bar as a value so the meaning would be to multiply T::bar by p which is not what we want.
The solution is to qualify the dependent type with the typename keyword.
typename T::bar * p;
This alerts the compiler to the fact that we intend to treat bar as a type.
There's only one spot where they differ (when declaring template parameters), and that is when using template-templates.
The following is well-defined C++
template <template <typename> class TT> struct example_one {};
while this is not:
template <template <typename> typename TT> struct example_two {};
Since it seems like you're just starting out with C++/templates, this corner case won't concern you for a while :-) Aside from the above, class template, function template, it doesn't matter: typename and class are synonymous.
I'm currently facing a problem I haven't been able to solve myself.
Basically what I'm trying to do is implement some linq-like behaviour in C++.
I'll start off with the code in my header:
template<typename T, template<class = T> class A,
template<class = T, template<class=T> class = A> class C>
class queryable
{
public:
typedef T value_type;
typedef A<value_type> allocator_type;
typedef C<value_type, allocator_type> container_type; // (1)
typedef queryable<T, A, C> type;
queryable(container_type const &) { }
template<typename _Out> queryable<_Out, A, C> select(/* some delegate */);
// more methods etc
}
And this is how I'd like it to be instantiated:
std::vector<int> my_vec;
queryable<std::vector<int> > q(my_vec);
Needless to say this doesn't work (otherwist I wouldn't be here :) )
Now the even stranger part is that even this doesn't seem to work:
std::vector<int> my_vec;
queryable<int, std::allocator, std::vector> q(my_vec);
As you can see (by looking at the select function), it is important to me to not just use something like this:
template<typename T> class queryable;
Any suggestions on how to solve this? And is this even possible?
Any help would be appreciated!
EDIT: the errors I'm getting:
../entry.cpp:19:58: error: type/value mismatch at argument 3 in template parameter list for ‘template<class T, template<class> class A, template<class, template<class> class<template-parameter-2-2> > class C> class failproof::collections::queryable’
../entry.cpp:19:58: error: expected a template of type ‘template<class, template<class> class<template-parameter-2-2> > class C’, got ‘template<class _Tp, class _Alloc> class std::vector’
../entry.cpp:19:61: error: invalid type in declaration before ‘;’ token
EDIT 2:
As far as I understand the compiler is complaining about C not taking 2 class arguments, but 1 class argument and 1 templated class argument (1), because I defined C to be that way.
Is there any way to resolve this issue?
There is a general method to 'explode' a type to test if it was created by a template, and to extract the types that were passed to that template. It is also possible to access the template itself and pass other parameters to it if you desire.
vector is a class template. When you apply parameters to it, you get something like vector<int>, which is a template class. A template class is a specific type, like any other type, it just happens to have been created via a class template.
The goal is, given a type T, to test if it is a template class, and if so to gain access to the class template that was used to create it, and also to access the parameters that were passed to the class template. In this sample, I just test for whether something is a one-arg or two-arg template, but the technique can easily be extended.
(Technically, vector is a two-arg template. There is a default for the second parameter, so vector<int> is actually vector<int, allocator<int> >, but it's still basically a two-arg template, not a one-arg template.)
The best place to start is with this sample code I've put on ideone. I'll copy the Exploder code at the end of this answer.
I begin with
typedef list<int> list_of_ints;
and proceed to use the Exploder template to access all the above information. For example, Exploder<list_of_ints> :: type_1 is the first parameter that was passed to the template, in this case int. The second parameter (this is the defaulted parameter) is allocator<int> and is accessible with Exploder<list_of_ints> :: type_2.
typedef Exploder<list_of_ints> :: type_2 should_be_an_allocator_int;
Given this second type, which we know was created by a template, we can access its parameter type, int, with Exploder< should_be_an_allocator_int > :: type_1, but it's more interesting to actually access the allocator template instead and pass a different parameter to it. This next line evaluates, in this example, to an allocator<double>.
typedef Exploder< should_be_an_allocator_int >
:: rebind<double> :: type should_be_an_allocator_double;
So, even if your list<...,...> type did not use the default allocator, you can access the allocator that was used, and also any class template that was used to create the allocator type.
Now that we have a suitable allocator, we can go back to our original template class list<int> and replace int with double:
Exploder<list_of_ints> :: rebind<double, should_be_an_allocator_double> :: type
To verify all this has worked, the sample code uses typeid(...).name() to print the actual type of the various objects, along with the correct type that it should be. You can see that they match.
(Also, some templates are such that their parameters are not types, but other class templates, or even other template templates. It should be possible to extract all that, but I'm not going to look into that here.)
(One last interesting technical note. Some types, such as allocator, have something called rebind to allow this sort of access. But the technique used above works for all template classes, even those without their own rebind)
The full code for the template Exploder
See sample code I've put on ideone for a full demo.
template <class>
struct Exploder;
template<class T, template<class> class Template>
struct Exploder< Template<T> > {
static const char * description() { return " One-arg template. Arg 1 is a type "; }
typedef T type_1;
template <class V>
struct rebind {
typedef Template<V> type;
};
};
template<class T, class U, template<class,class> class Template>
struct Exploder< Template<T,U> > {
static const char * description() { return " Two-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef T type_1;
typedef U type_2;
template <class V,class W>
struct rebind {
typedef Template<V,W> type;
};
};
template<class S, class T, class U, template<class,class,class> class Template>
struct Exploder< Template<S,T,U> > {
static const char * description() { return " Three-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef S type_1;
typedef T type_2;
typedef U type_3;
};
The second template parameter of the standard containers (the allocator) is a type, not a template, so you need to change your third parameter to something like
template<typename, typename> class C
(Note that the default arguments in your template parameter specifications don't serve any purpose, so I omitted them here.)
Then you should be able to instantiate the template as
queryable<int, std::allocator, std::vector>
You may be better off just parametrising over the container type, and then using its value_type and allocator_type definitions:
template <typename C> class queryable
{
public:
typedef typename C::value_type value_type;
typedef typename C::allocator_type allocator_type;
typedef C container_type;
};
(One downside is that you can't directly access the allocator template; however, you can use the allocator type's nested rebind definition if you need to instantiate that template for other types.)
Also, typedef iterator const const_iterator; is wrong; that declares an unmodifiable iterator that can be used to modify the sequence, while const_iterator is supposed to be a modifiable iterator that can't be used to modify the sequence.
(A note on terminology. vector is a class template, i.e. without any parameters. And vector<int> is a template class, i.e. a class almost like any other except that it was created by a template.)
It is possible to use it as you wish, where queryable takes one template parameter:
queryable< vector<int> > q;
This means that querable is a template with just one parameter:
template <typename T>
struct queryable;
You then use a specialization that has more than one parameter:
template <typename ValueType, template<class T,class = allocator<T> > class ContainerTemplate>
struct queryable< ContainerTemplate<Contained> > {
typedef ValueType value_type;
typedef ContainerTemplate<ValueType> container_type;
typedef typename container_type :: allocator_type A;
// typedef ContainerTemplate <WhateverOtherValueTypeYouWish> ...
// typedef A :: rebind <SomeOtherType> ...
};
The last two lines, commented out, show how you can use ContainerTemplate as a class template, creating other types as required. ContainerTemplate is vector or list or set or something like that.
As #MikeSeymour has pointed out, the use of rebind might be the way to access the allocator class template.
Sample code on ideone
Can alias templates (14.5.7) be explicitly specialised (14.7.3)?
My standard-fu fails me, and I can't find a compiler to test on.
The text "when a template-id refers to the specialization of an alias template" implies yes, but then the example appears to refer to something else, implying no.
NB. I'm working from n3242, one behind the FDIS, in which the title of this section is "Aliase templates". Lol.
What the Standard means by "specialization" is the transformation of a generic template to a more specialized entity. For example, instantiating a non-member class template yields a class that's not a template anymore. The term "specialization" is two fold, and can refer to a generated specialization (which is a specialization that was instantiated, possibly from a partial specialization) and to an explicit specialization (which is what you referred to).
Alias templates aren't instantiated and there aren't specializations of them. There is nothing they could instantiate to. Instead, whenever their name is followed by a template argument list, the type denoted is the type you get by replacing the name and argument list by the alias'ed type, replacing all template parameter references with the arguments given in the argument list. That is, rather than the specialization of it being an alias, the alias template itself serves as an alias, without the need to instantiate anything. This replacement is done very early. Consider:
template<typename T> using ref = T&;
template<typename T> void f(ref<T> x) { x = 10; }
int main() { int a; f(a); return a; /* 10 */ }
The replacement is done at the time ref<T> is named (such a names are used to refer to class or function template specializations; hence the spec describes such names to "refer to the specialization of an alias template"). That is, the parameter of f has type T&, and thus, T can be deduced. This property is preventing explicit or partial specializations of alias templates. Because in order to pick the correct specialization of ref, it needs to know T. But to know it, it needs to compare ref<T> against the argument type to deduce T. It's summarized in the paper N1406, "Proposed addition to C++: Typedef Templates", section 2.2
2.2 The Main Choice: Specialization vs. Everything Else
After discussion on the reflectors and in the Evolution WG, it turns out that we have to choose between two mutually exclusive models:
A typedef template is not itself an alias; only the (possibly-specialized) instantiations of the typedef template are aliases. This choice allows us to have specialization of typedef templates.
A typedef template is itself an alias; it cannot be specialized. This choice would allow:
deduction on typedef template function parameters (see 2.4)
a declaration expressed using typedef templates be the same as the declaration without
typedef templates (see 2.5)
typedef templates to match template template parameters (see 2.6)
It should be noted that the quoted paper, which favors option 1, did not make it into C++0x.
EDIT: Because you desperately want to have a spec quote saying so explicitly. 14.5p3 is one that does
Because an alias-declaration cannot declare a template-id, it is not possible to partially or explicitly specialize an alias template.
Bjarne says:
Specialization works (you can alias a set of specializations but you cannot specialize an alias)
And, whilst not an explicit rule, "alias templates" are missing from the following list at 14.7.3/1:
An explicit specialization of any of the following:
function template
class template
member function of a class template
static data member of a class template
member class of a class template
member class template of a class or class template
member function template of a class or class template
can be declared[...]
I think that this is the best guarantee you'll get.
If you need pointwise mapping from something to types, this works(in gcc 4.8.3):
// int to type mapper
template<int BITS>
struct BitsToTypesMap
{
typedef void TYPE; // default
};
// pointwise mapping
template<>
struct BitsToTypesMap<32>{ typedef int TYPE; };
template<>
struct BitsToTypesMap<8>{ typedef char TYPE; };
template<>
struct BitsToTypesMap<16>{ typedef short TYPE; };
// cute wrapping
template<int BITS> using MyScalarType = typename BitsToTypesMap<BITS>::TYPE;
// TEST
template<int BITS>
MyScalarType<BITS>
Add ( MyScalarType<BITS> x, MyScalarType<BITS> y )
{
return x+y;
}
int
test()
{
MyScalarType<32> i=Add<32>(1,2);
MyScalarType<8 > b=Add<8 >(1,2);
MyScalarType<16> s=Add<16>(1,2);
return i+b+s;
}
I am not sure if I understand the question, but in any case I tried to simulate specialization of alias template.
I assume that the idea is to restrict the alias template to certain (pattern matched type); something that we used to do with this sort of code:
template<class Vector> struct old_style;
template<class T> struct old_style<std::vector<T> >{
typedef typename std::vector<T>::value_type type;
};
(this is just an example, there are other way to extract the value_type of a generic std::vector).
Now to the aliases:
template<class Vector> using new_style = typename Vector::value_type;
It does the same work, but this does not replace old_stype<...>::type, since it is not as restrictive. The first try to have a perfect alias replacement is this hypothetical code:
//template<class Vector> using new_style2; // error already here
//template<class T> using new_style2<std::vector<T> > = typename Vector::value_type;
Unfortunately it doesn't compile (in theory because of nominal reasons stated in other answers and in the standard, in practice I guess there is no fundamental reason for this to be a limitation). Fortunately one can fallback to the old fashioned struct::type way of doing it an only use the new alias template feature to forward the work,
template<class Vector> struct new_style2_aux;
template<class T> struct new_style2_aux<std::vector<T> >{
typedef typename std::vector<T>::value_type type;
};
template<class Vector> using new_style2 = typename new_style2_aux<Vector>::type;
One can make it automatic with a define
#define SPECIALIZED_ALIAS_TEMPLATE(NamE, Pattern_arG, PatterN_expR, DefinitioN) \
template<class> struct NamE ## _aux; \
template<Pattern_arG> struct NamE ## _aux<PatterN_expR>{ \
typedef DefinitioN type; \
}; \
template<class NamE ## _dummy> using NamE = typename NamE ## _aux< NamE ## _dummy >::type;
Which can be used as:
SPECIALIZED_ALIAS_TEMPLATE(new_style3, class T, std::vector<T>, typename std::vector<T>::value_type);
If one needs an arbitrary number of specializations (or non local in the code), one has to use a more complicated define in two parts, one for declaring and one for specializing (as it should be):
#define DECLARE_ALIAS_TEMPLATE(NamE)\
template<class> struct NamE ## _aux;\
template<class NamE ## _dummy> using NamE = typename NamE ## _aux< NamE ## _dummy >::type;
#define SPECIALIZE_ALIAS_TEMPLATE(NamE, Pattern_arG, PatterN_expR, DefinitioN)\
template<Pattern_arG> struct NamE ## _aux<PatterN_expR>{ \
typedef DefinitioN type; \
};
Used as follows:
DECLARE_ALIAS_TEMPLATE(new_style4);
SPECIALIZE_ALIAS_TEMPLATE(new_style4, class T, std::vector<T>, typename std::vector<T>::value_type);
SPECIALIZE_ALIAS_TEMPLATE(new_style4, class T, std::set<T>, typename std::set<T>::value_type);
All the code above can be copied and pasted to test:
#include<vector>
#include<map>
// ... paste code above //
int main(){
old_style<std::vector<double> >::type a; // is a double
// old_style<std::set<double> >::type a; // error (should work only for std::vector)
new_style2<std::vector<double> > b; // is double
// new_style2<std::set<double> > c; // error (should work only for std::vector)
new_style3<std::vector<double> > d; // is double
// new_style3<std::set<double> > d; // error (should work only for std::vector)
new_style4<std::vector<double> > e; // is double
new_style4<std::set<double> > f; // is double, this is another specialization
return 0;
}
Sorry if this is not what you are looking for. I believe it can be used with variadic templates, and with extra template arguments (in the specialization) but didn't test it.
Improvements are very welcomed.