Template aliases and dependent names
c++, c++11, language-lawyer, templates
Solution
Class templates and function templates are instantiated, but alias templates are simply substituted. And by getting rid of the member name `type`, you lose a chance at invoking the dependent name lookup rules.
[N3285] 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.
So in the first case, you have:
The definition of `struct derived` requires the implicit instantiation of `base<derived>`. During this instantiation, we discover `base<derived>` has a member function template:
template <typename Delayer=void>
typename derived_value_type<derived, Delayer>::type foo();
The return type is dependent, so `type` is not yet looked up, and no specialization of `derived_value_type` is instantiated. The instantiation finishes, and `base<derived>` and `derived` are both now complete types.
In `main`, the expression `d.foo()` requires the implicit instantiation of `base<derived>::foo<void>()`. Now the name `typename derived_value_type<derived, void>::type` is looked up, instantiating `derived_value_type<derived, void>` along the way. The return type is found to be `int`.
In the second case, `derived_value_type` is not a dependent name, so is bound to your alias template declaration at the definition of template `base<D>`. The compiler could do the alias substitution either at the template definition or during each instantiation of the class, but either way you get a class template equivalent to:
template <typename Derived>
struct base
{
template <typename Delayer = void>
typename Derived::value_type
foo(){ return {}; }
};
The definition of `struct derived` requires the implicit instantiation of `base<derived>`. During this instantiation, we discover `base<derived>` has a member function template:
template <typename Delayer=void>
typename derived::value_type foo();
But `derived::value_type` is not dependent, and `derived` is an incomplete type, so the code is ill-formed.
Problem
While thinking how CRTP can be improved in C++11, I ended with the following code: ``` template <typename Derived, typename Delayer> struct derived_value_type { typedef typename Derived::value_type type; }; template <typename Derived> struct base { template <typename Delayer = void> typename derived_value_type<Derived, Delayer>::type foo(){ return {}; } }; struct derived : base<derived> { typedef int value_type; }; #include <iostream> #include <typeinfo> int main() { derived d; auto bar = d.foo(); std::cout << typeid(bar).name() << ':' << bar << std::endl; } ``` I believe the previous code to be standard conformant, and it compiles and works with the major compilers (resulting in `i:0`). However, when I use a template alias instead, I get a compilation error due to `derived` being incomplete: ``` template <typename Derived, typename Delayer> using derived_value_type = typename Derived::value_type; /*...*/ template <typename Delayer = void> derived_value_type<Derived, Delayer> foo(){ return {}; } ``` Is this a compiler bug, or does the fact that the compiler can determinate that there is no real dependency with `Delayer` mean that the template alias is not a dependent type? Where is this specified in the standard?