模板别名和从属名称

时间:2014-01-28 02:33:49

标签: c++ templates c++11 language-lawyer

在考虑如何在 C ++ 11 中改进 CRTP 时,我结束了以下代码:

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:0)。但是,当我使用模板别名时,由于derived不完整而导致编译错误:

template <typename Derived, typename Delayer>
using derived_value_type = typename Derived::value_type;

/*...*/

template <typename Delayer = void>
derived_value_type<Derived, Delayer>
foo(){ return {}; }

这是编译器错误,还是编译器可以确定与Delayer没有真正依赖关系的事实意味着模板别名不是依赖类型?标准中指定的是哪里?

1 个答案:

答案 0 :(得分:4)

实例化类模板和函数模板,但简单地替换别名模板。通过删除成员名称type,您将失去调用依赖名称查找规则的机会。

[N3285] 14.5.7p2:

  

template-id 引用别名模板的特化时,它等同于通过将 template-arguments 替换为 type-id 中的> template-parameters 。

所以在第一种情况下,你有:

struct derived的定义需要base<derived>的隐式实例化。在此实例化期间,我们发现base<derived>具有成员函数模板:

template <typename Delayer=void>
typename derived_value_type<derived, Delayer>::type foo();

返回类型是依赖的,因此尚未查找type,并且未实例化derived_value_type的特化。实例化完成,base<derived>derived现在都是完整类型。

main中,表达式d.foo()需要隐式实例化base<derived>::foo<void>()。现在查找名称typename derived_value_type<derived, void>::type,沿途实例化derived_value_type<derived, void>。返回类型为int

在第二种情况下,derived_value_type不是依赖名称,因此在模板base<D>的定义中绑定到别名模板声明。编译器可以在模板定义或类的每个实例化期间进行别名替换,但无论哪种方式,您都可以获得等同于的类模板:

template <typename Derived>
struct base
{
  template <typename Delayer = void>
  typename Derived::value_type
  foo(){ return {}; }
};

struct derived的定义需要base<derived>的隐式实例化。在此实例化期间,我们发现base<derived>具有成员函数模板:

template <typename Delayer=void>
typename derived::value_type foo();

derived::value_type不依赖,derived是不完整的类型,因此代码格式不正确。