Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

c++11-17 模板核心知识(十五)—— 解析模板之依赖型类型名称(Dependent Names of Types)与typename #165

Open
zhangyachen opened this issue Dec 8, 2020 · 0 comments
Labels

Comments

@zhangyachen
Copy link
Owner

上篇文章c++11-17 模板核心知识(十四)—— 解析模板之依赖型模板名称 Dependent Names of Templates(.template/->template/::template) 介绍了依赖型模板名称,提到关于模板解析有六个大方面:

  • 非模板中的上下文相关性 Context Sensitivity in Nontemplates
  • 依赖型类型名称 Dependent Names of Types <-----
  • 依赖型模板名称 Dependent Names of Templates
  • using-declaration中的依赖型名称 Dependent Names in Using Declarations
  • ADL和显式模板实参 ADL and Explicit Template Arguments
  • 依赖性表达式 Dependent Expressions

这篇文章介绍下依赖型类型名称(Dependent Names of Types)。

模板名称的问题及解决

模板中的名称存在一个问题:它们有的时候不能被很好的分类,比如一个模板引用其他模板的名称,因为模板特化的存在,会让问题变得复杂一些。例如:

template <typename T> class Trap {
public:
  enum { x }; // #1 x is not a type here
};

template <typename T> class Victim {
public:
  int y;
  void poof() {
    Trap<T>::x *y; // #2 declaration or multiplication?
  }
};

template <> class Trap<void> { // evil specialization!
public:
  using x = int; // #3 x is a type here
};

void boom(Victim<void> &bomb) { bomb.poof(); }

如果你直接编译,会报错:

main.cc:30:14: error: unexpected type name 'x': expected expression
    Trap<T>::x *y; // #2 declaration or multiplication?
             ^
main.cc:39:38: note: in instantiation of member function 'Victim<void>::poof' requested here
void boom(Victim<void> &bomb) { bomb.poof(); }
                                     ^
1 error generated.

这个问题和解决方案在c++11-17 模板核心知识(二)—— 类模板涉及过,这篇文章再展开说一下相关规则。

回到上面的例子,当编译器解析到#2处时,它需要决定Trap<T>::x是一个类型还是一个值,这决定了Trap<T>::x *y是声明一个指针还是做乘法。

问题是,在Trap中,Trap<T>::x是一个值,但是在全特化版本Trap<void>中,Trap<T>::x是一个类型。所以,这种情况实际是依赖模板参数T的,也就是依赖型类型名称(Dependent Names of Types)。

C++规定,只有当加上typename关键字后,依赖型类型名称才会被当做类型,否则会被当做一个值。这里typename的意义和声明一个模板时使用的typename是两个意思,所以不能用class来替换typename.

typename规则

当一个名称具备以下性质时,需要在名称前面加typename:

  • 是qualified name。
  • 不是Elaborated type specifier的一部分(例如,以class、struct、union、enum为开头的类型)
  • 名称不是用于指定基类继承的列表中,也不是位于引入构造函数的成员初始化列表中。
  • 依赖于模板参数。

例如:

template <typename(1) T> 
struct S : typename(2) X<T>::Base {
  S() : typename(3) X<T>::Base(typename(4) X<T>::Base(0)) {}
  
  typename(5) X<T> f() {
    typename(6) X<T>::C *p; // declaration of pointer p
    X<T>::D *q;           // multiplication!
  }
  typename(7) X<int>::C *s;

  using Type = T;
  using OtherType = typename(8) S<T>::Type;
};

下面逐一说下上面各个typename的使用场景(有的使用方式是错误的):

  • 第一个typename代表一个模板参数,不在此文章讨论范围内。
  • 第二和第三个typename是错误的使用方式,不需要添加,违反了上面的第3条规则。第二个出现在了指定基类继承的列表中,第三个出现在了构造函数的成员初始化列表。如果加上typename编译,会报如下错误:
main.cc:30:12: error: 'typename' is redundant; base classes are implicitly types
struct S : typename X<T>::Base {
           ^~~~~~~~~
  • 第四个typename是必须的,它满足上面第3条规则,且其他规则也满足。
  • 第五个typename是错误的,因为X不是一个qualified name,如果加上typename编译,会报:
main.cc:33:12: error: expected a qualified name after 'typename'
  typename X<T> f() {
           ^
  • 第六个typename是必须的,上面讲过,代表一个类型。
  • 第七个typename是可有可无的,因为X<int>::C不依赖模板参数,即不是Dependent Name.
  • 第八个typename也是可有可无的,因为它指向的是current instantiation,这个概念下篇文章会讲到。

C++20 typename

是了,这一大堆乱七八糟的规则,谁也不想去记。C++20对typename的规则做了一些改善,有一些场景不再需要typename。详情大家可以参考 : The typename disambiguator for dependent names

image

(完)

朋友们可以关注下我的公众号,获得最及时的更新:

image

@zhangyachen zhangyachen added the cpp label Dec 8, 2020
@zhangyachen zhangyachen changed the title c++11-17 模板核心知识(十五)—— 解析模板之依赖型类型名称与typename Dependent Names of Types c++11-17 模板核心知识(十五)—— 解析模板之依赖型类型名称(Dependent Names of Types)与typename Dec 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant