问题
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?
回答1:
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.
来源:https://stackoverflow.com/questions/21395786/template-aliases-and-dependent-names