Clan behavior is correct.
A::A_Type equivalent to int in accordance with [7.1.3p1] in the standard:
[...] As part of its declaration, the name typedef is syntactically equivalent to a keyword and names the type associated with the identifier in the method described in clause 8. Thus, typedef-name is a synonym for another type. The typedef name does not introduce a new type of class declaration (9.1) or enumeration declaration.
A::A_Templated<int> equivalent to Templated<int> according to [14.5.7p2]:
When the template identifier refers to the specialization of the alias template, this is equivalent to the associated type obtained by replacing its template arguments for the template parameters in the template identifier of the alias template type.
However, A::A_Templated not equivalent to Templated , according to [14.5.7p1]:
[...] The nickname template name is the name of the template.
This means that A::A_Templated and Templated are two different templates, so Test_Templated<A::A_Templated> and Test_Templated<Templated> are different specializations of Test_Templated , so casts that return null pointers are correct.
GCC 5.1.0 does not handle this correctly. Clang 3.6.0 and MSVC 14 RC handle it correctly.
All references to the working draft N4431.
Note that there is an active issue with the main working group, Issue 1286, regarding this behavior. The author says that the intention is to introduce a standard wording so that such things work as you expected, i.e. Make the alias template equivalent to the one specified in the type identifier. There is a note dated May 2015, indicating that the problem is attracting attention, but it does not exist yet.
In terms of โgetting it working,โ itโs hard to make decisions without knowing what your practical needs are, but I will try to make Test_Templated dependent on the Templated specialization, not the template itself, which declares it as
template<class T> class Test_Templated : public Test_base { };
and use it like
test = new Test_Templated<Templated<int>>; std::cout << dynamic_cast< Test_Templated<Templated<int>>* >(test) << std::endl;
You can wrap this up by adding a level of indirection if that helps anyway:
template<template<class> class TT, class T> using Make_Test_Templated = Test_Templated<TT<T>>;
and then use it like this:
test = new Make_Test_Templated<A::A_Templated, long>; std::cout << dynamic_cast< Make_Test_Templated<A::A_Templated, long>* >(test) << std::endl;
In any case, I think the key is to try to use the fact that the specializations are equivalent.
Well, based on your last update, here is the hack that deals with the problem in the second code example: change the explicit specialization B<Templated> to a partial specialization, which only matches if you specify a template that generates the same specialization as Templated when instantiating with a specific argument (e.g. int for this example).
How is this for a confusing sentence? I'm sorry. Here, what your sample code will be with the above changes:
#include <iostream> #include <type_traits> template<class> class Templated { }; template<class T> using Templated_alias = Templated<T>; template<class> class Templated2 { }; // Helper trait template<template<class> class TT1, template<class> class TT2> using is_same_template_t = typename std::is_same<TT1<int>, TT2<int>>::type; template<template<class> class, class = std::true_type> class B; template<template<class> class TT> class B<TT, is_same_template_t<TT, Templated>> { public: void foo(Templated<int>) { std::cout << "B<Templated>::foo\n"; } }; int main() { B<Templated> b1; b1.foo(Templated<int>()); b1.foo(Templated_alias<int>()); B<Templated_alias> b2; // Works fine now, and so do the next two lines. b2.foo(Templated<int>()); b2.foo(Templated_alias<int>()); // B<Templated2> b22; // Error trying to instantiate the primary template B. }
Note that you must ensure that is_same_template_t is only used to check for patterns that can be created using the int argument (change int to whatever you need, of course). If you want to make it more universal, you can also specify the type in which templates should be created in the list of characteristic parameters, for example:
template<template<class> class TT1, template<class> class TT2, class T> using is_same_template_t = typename std::is_same<TT1<T>, TT2<T>>::type;
and use it as follows:
is_same_template_t<TT, Templated, int>