正式,什么是typename?

dic*_*oce 116 c++ templates

有时我看到gcc在使用模板时吐出的一些非常难以理解的错误消息......具体来说,我遇到了一些问题,看似正确的声明引起了非常奇怪的编译错误,通过在"typename"关键字前加上前缀而神奇地消失了声明的开头...(例如,就在上周,我宣布两个迭代器作为另一个模板化类的成员,我必须这样做)...

关于typename的故事是什么?

Nav*_*een 186

以下是Josuttis书中的引用:

引入关键字typename以指定后面的标识符是一种类型.请考虑以下示例:

template <class T>
Class MyClass
{
  typename T::SubType * ptr;
  ...
};
Run Code Online (Sandbox Code Playgroud)

这里,typename用于阐明SubType是类T的类型.因此,ptr是指向类型T :: SubType的指针.如果没有typename,SubType将被视为静态成员.从而

T::SubType * ptr
Run Code Online (Sandbox Code Playgroud)

将是类型T的值SubType与ptr的乘法.

  • 很棒的书。如果您愿意,请通读一遍,然后将其作为参考。 (2认同)
  • 精明的读者会意识到成员声明的语法不允许使用乘法表达式。因此,C++20 [不需要](https://en.cppreference.com/w/cpp/language/dependent_name#The_typename_disambiguator_for_dependent_names) 这个“类型名称”(尽管不是全部!)。 (2认同)

Xin*_*nus 35

Stan Lippman的BLog帖子建议: -

Stroustrup 重用现有的class关键字来指定一个类型参数,而不是引入一个当然可能破坏现有程序的新关键字.这不是一个新的关键词没有被考虑 - 只是因为它可能被破坏而被认为是不必要的.而直到ISO-C++标准,这是声明的类型参数的唯一途径.

因此,基本上Stroustrup重用了class关键字而没有引入一个新的关键字,后来在标准中进行了更改,原因如下

作为给出的例子

template <class T>
class Demonstration {
public:
void method() {
    T::A *aObj; // oops …
     // …
};
Run Code Online (Sandbox Code Playgroud)

语法语法错误解释T::A *aObj;为算术表达式,因此引入了一个新的关键字typename

typename T::A* a6;
Run Code Online (Sandbox Code Playgroud)

它指示编译器将后续语句视为声明.

由于关键字在工资单上,所以,为什么不修复由原始决策重用class关键字引起的混乱.

这就是为什么我们两个都有

你可以看看这篇文章,它肯定会对你有所帮助,我只是尽可能地从它中提取

  • @Jesper:我认为Xenus的回答令人困惑."typename"成为修复解析问题所必需的,如Naveen的回答所引用Josuttis所述.(我不认为在这个地方插入一个`class`就行了.)只有在这个案例接受new关键字之后,才允许在模板参数声明中使用(_或是定义?_),因为那个`课堂上总是有些误导. (5认同)

moo*_*dow 13

考虑一下代码

template<class T> somefunction( T * arg )
{
    T::sometype x; // broken
    .
    .
Run Code Online (Sandbox Code Playgroud)

不幸的是,编译器不需要是通灵的,并且不知道T :: sometype是否最终会引用T的类型名称或静态成员.因此,人们用typename它来告诉它:

template<class T> somefunction( T * arg )
{
    typename T::sometype x; // works!
    .
    .
Run Code Online (Sandbox Code Playgroud)


AnT*_*AnT 6

在某些你引用所谓依赖类型的成员(意思是"依赖于模板参数")的情况下,编译器不能总是明确地推断出结果构造的语义含义,因为它不知道是什么类型的名称. (即它是类型名称,数据成员名称还是其他名称).在这种情况下,您必须通过明确告诉编译器该名称属于定义为该依赖类型成员的类型名来消除歧义.

例如

template <class T> struct S {
  typename T::type i;
};
Run Code Online (Sandbox Code Playgroud)

在此示例typename中,代码编译所需的关键字.

当您想要引用依赖类型的模板成员(即指定模板的名称)时,会发生同样的情况.您还必须使用关键字来帮助编译器template,尽管它的位置不同

template <class T> struct S {
  T::template ptr<int> p;
};
Run Code Online (Sandbox Code Playgroud)

在某些情况下,可能有必要同时使用两者

template <class T> struct S {
  typename T::template ptr<int>::type i;
};
Run Code Online (Sandbox Code Playgroud)

(如果我正确地得到了语法).

当然,关键字的另一个角色typename是在模板参数声明中使用.


Phi*_*gan 5

两种用途:

  1. 作为template参数关键字(而不是class
  2. typename关键字告诉编译器的标识符是一种类型的(而不是静态成员变量)
template <typename T> class X  // [1]
{
    typename T::Y _member;  // [2] 
}
Run Code Online (Sandbox Code Playgroud)


phl*_*psy 5

秘密在于,模板可以专门用于某些类型。这意味着它还可以为几种类型定义完全不同的接口。例如,您可以编写:

template<typename T>
struct test {
    typedef T* ptr;
};

template<>         // complete specialization 
struct test<int> { // for the case T is int
    T* ptr;
};
Run Code Online (Sandbox Code Playgroud)

有人可能会问,为什么这样做有用,并且确实如此:看起来真的没用。但是请记住,例如std::vector<bool>reference类型看起来与其他类型完全不同T。诚然,它不会将类型reference从类型更改为其他内容,但是仍然可能发生。

现在,如果您使用此test模板编写自己的模板,将会发生什么。像这样

template<typename T>
void print(T& x) {
    test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

这似乎对您来说还可以,因为您希望test<T>::ptr是一种类型。但是编译器不知道,事实上,甚至标准建议他甚至都希望相反,test<T>::ptr这不是一种类型。要告诉编译器您期望什么,您必须先添加一个typename。正确的模板如下所示

template<typename T>
void print(T& x) {
    typename test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

底线:typename每当在模板中使用模板的嵌套类型时,都必须在添加之前。(当然,仅当您的模板的模板参数用于该内部模板时才如此。)