模板别名和因名称

Template aliases and dependent names

本文关键字:别名      更新时间:2023-10-16

在思考 crtp 如何改进的同时,在 c 11 中,我以以下代码结尾:

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没有真正的依赖性的事实意味着模板别名不是依赖类型?标准中在哪里指定?

类模板和函数模板是实例化的,但是别名模板是简单地取代的。通过摆脱会员名称type,您将失去调用依赖名称查找规则的机会。

[N3285] 14.5.7p2:

当A template-id 指的是别名模板的专业化时,它等同于通过替换其 template-arguments 获得的相关类型>模板模板中的模板 - 参数。

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

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是不完整的类型,因此代码不正确。