I hope the title makes sense. I probably miss vocabulary to express it correctly.
Well, an exemple will probably be more clear.
Problem for me is: dynamic do
Clang's behaviour is correct.
A::A_Type
is equivalent to int
according to [7.1.3p1] in the standard:
[...] Within the scope of its declaration, a typedef-name is syntactically equivalent to a keyword and names the type associated with the identifier in the way described in Clause 8. A typedef-name is thus a synonym for another type. A typedef-name does not introduce a new type the way a class declaration (9.1) or enum declaration does.
A::A_Templated<int>
is equivalent to Templated<int>
according to [14.5.7p2]:
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.
However, A::A_Templated
is not equivalent to Templated
, according to [14.5.7p1]:
[...] The name of the alias template is a template-name.
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
, thus the casts that return null pointers are correct in doing so.
GCC 5.1.0 doesn't handle this correctly. Clang 3.6.0 and MSVC 14 RC handle it correctly.
All references are to working draft N4431.
Note that there is an active Core Working Group Issue regarding this behaviour - Issue 1286. The author says that the intention is to introduce standard wording to make such cases work the way you expected, that is, make the alias template equivalent to the one referenced in the type-id. There's a note from May 2015 in there, indicating that the issue is receiving attention, but it's not there yet.
In terms of "making it work", it's difficult to give solutions without knowing what your practical needs are, but I'd try to make Test_Templated
depend on specializations of Templated
, rather than the template itself, that is, declare it like
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; //ok
std::cout << dynamic_cast< Test_Templated<A::A_Templated<int>>* >(test) << std::endl; //also ok
You could wrap this by adding a level of indirection, if that helps in any way:
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; //ok
std::cout << dynamic_cast< Make_Test_Templated<Templated, long>* >(test) << std::endl; //also ok
Anyway, I think the key is to try to use the fact that the specializations are equivalent.
Alright, based on your latest update, here's a hack addressing the problem in your second code sample: change the explicit specialization B<Templated>
to a partial specialization that only matches if given a template that generates the same specialization as Templated
when instantiated with a certain argument (let's say int
for this example).
How's that for a confusing sentence? Sorry. Here's what your code sample becomes 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 have to make sure is_same_template_t
is only used to check templates that can be instantiated with an int
argument (change int
to whatever you need, of course). If you want to make it more generic, you can also include the type on which the templates need to be instantiated in the trait's parameter list, like this:
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 like this:
is_same_template_t<TT, Templated, int>