Assuming we have
template<typename T>
struct Foo
{
};
template<typename T>
struct Bar
{
};
template< template<typename T> class C >
struct Pack
{
template<typename T>
using Container = C<T>;
};
Are Foo
and Pack<Foo>::Container
supposed to be treated as the same thing if we have a specialization for Foo
? That is:
template< template<typename T> class C >
struct IsFoo : std::false_type
{
};
template<>
struct IsFoo<Foo> : std::true_type
{
};
static_assert( IsFoo<Foo>::value, "Only foos!" );
static_assert( IsFoo< Pack<Foo>::Container >::value, "Only foos!" ); // ???
static_assert( IsFoo< Pack<Bar>::Container >::value == false, "Not a foo!" );
Is that second assertion correct? What is the expected behavior? Heck, is what I am trying to do even valid?
I tested it on three compilers and I got different results. It seems that for
MSVC and CLang, Foo
and Pack<Foo>::Container
are not the same thing, but GCC disagrees, which is cool, because that is exactly what I wanted.
So, who is right?
PS: I am not sure if I am using the correct terminology on the title, nor inside the body of my question. Suggestions and corrections are more than welcome.
Alias templates are a way to give a name to a family of types. Template parameters can be types, non-types, and templates themselves.
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
A reference variable is an alias, that is, another name for an already existing variable. Once a reference is initialized with a variable, either the variable name or the reference name may be used to refer to the variable.
Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type. In C++ this can be achieved using template parameters.
14.5.7 Alias templates
1 A template-declaration in which the declaration is an alias-declaration (Clause 7) declares the identifier to be a alias template. An alias template is a name for a family of types. The name of the alias template is a template-name.
2 When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.
In your example Foo
and Pack<Foo>::Container
(no parameter list) are template-names that don't represent a type but only a template. For example Foo<int>
and Pack<Foo>::Container<int>
would be template-ids and would thus be equivalent.
To my knowledge the standard doesn't specify any equivalence relation between template-names, so MSVC and Clang are right to assume that only identical template-names are equivalent.
But you could still specialize or overload based on the template-id if that is sufficient for your specific use case. e.g.:
template< typename C >
struct IsFoo : std::false_type
{
};
template<class T>
struct IsFoo<Foo<T>> : std::true_type
{
};
static_assert( IsFoo<Foo<int>>::value, "Only foos!" );
static_assert( IsFoo< Pack<Foo>::Container<int> >::value, "Only foos!" );
static_assert( IsFoo< Pack<Bar>::Container<int> >::value == false, "Not a foo!" );
or
template<class T>
void do_stuff(const T&) {}
template<class T>
void do_stuff(const Foo<T>&) {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With