C++ 正式地说,typename 是做什么用的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1600936/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:34:40  来源:igfitidea点击:

Officially, what is typename for?

c++templates

提问by dicroce

On occasion I've seen some really indecipherable error messages spit out by gccwhen using templates... Specifically, I've had problems where seemingly correct declarations were causing very strange compile errors that magically went away by prefixing the typenamekeyword to the beginning of the declaration... (For example, just last week, I was declaring two iterators as members of another templated class and I had to do this)...

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

What's the story on typename?

讲的是什么故事typename

回答by Naveen

Following is the quote from Josuttis book:

以下是 Josuttis 书中的引述:

The keyword typenamewas introduced to specify that the identifier that follows is a type. Consider the following example:

template <class T>
Class MyClass
{
  typename T::SubType * ptr;
  ...
};

Here, typenameis used to clarify that SubTypeis a type of class T. Thus, ptris a pointer to the type T::SubType. Without typename, SubTypewould be considered a static member. Thus

T::SubType * ptr

would be a multiplication of value SubTypeof type Twith ptr.

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

template <class T>
Class MyClass
{
  typename T::SubType * ptr;
  ...
};

在这里,typename用于说明它 SubTypeclass T. 因此, ptr是一个指向类型的指针 T::SubType。没有typename,SubType将被视为静态成员。因此

T::SubType * ptr

将是SubType类型值T与的乘积 ptr

回答by Xinus

Stan Lippman's BLog postsuggests :-

Stan Lippman 的博客帖子建议:-

Stroustrup reused the existing class keywordto specify a type parameter rather than introduce a new keyword that might of course break existing programs. It wasn't that a new keyword wasn't considered -- just that it wasn't considered necessary given its potential disruption. And up until the ISO-C++ standard, this was the only way to declare a type parameter.

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

So basically Stroustrup reused class keyword without introducing a new keyword which is changed afterwards in the standard for the following reasons

所以基本上 Stroustrup 重用了 class 关键字而不引入新的关键字,由于以下原因,该关键字后来在标准中发生了变化

As the example given

正如给出的例子

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

language grammar misinterprets T::A *aObj;as an arithmetic expression so a new keyword is introduced called typename

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

typename T::A* a6;

it instructs the compiler to treat the subsequent statement as a declaration.

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

Since the keyword was on the payroll, heck, why not fix the confusion caused by the original decisionto reuse the class keyword.

既然关键字在工资单上,见鬼,为什么不解决由重用 class 关键字的原始决定引起的混乱

Thats why we have both

这就是为什么我们有两个

You can have a look at this post, it will definitely help you, I just extracted from it as much as I could

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

回答by moonshadow

Consider the code

考虑代码

template<class T> somefunction( T * arg )
{
    T::sometype x; // broken
    .
    .

Unfortunately, the compiler is not required to be psychic, and doesn't know whether T::sometype will end up referring to a type name or a static member of T. So, one uses typenameto tell it:

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

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

回答by AnT

In some situations where you refer to a member of so called dependenttype (meaning "dependent on template parameter"), the compiler cannot always unambiguously deduce the semantic meaning of the resultant construct, because it doesn't know what kind of name that is (i.e. whether it is a name of a type, a name of a data member or name of something else). In cases like that you have to disambiguate the situation by explicitly telling the compiler that the name belongs to a typename defined as a member of that dependent type.

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

For example

例如

template <class T> struct S {
  typename T::type i;
};

In this example the keyword typenamein necessary for the code to compile.

在这个例子中,关键字typename是编译代码所必需的。

The same thing happens when you want to refer to a template member of dependent type, i.e. to a name that designates a template. You also have to help the compiler by using the keyword template, although it is placed differently

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

template <class T> struct S {
  T::template ptr<int> p;
};

In some cases it might be necessary to use both

在某些情况下,可能需要同时使用

template <class T> struct S {
  typename T::template ptr<int>::type i;
};

(if I got the syntax correctly).

(如果我的语法正确)。

Of course, another role of the keyword typenameis to be used in template parameter declarations.

当然,关键字的另一个作用typename是用于模板参数声明。

回答by phlipsy

The secret lies in the fact that a template can be specialized for some types. This means it also can define the interface completely different for several types. For example you can write:

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

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

template<>         // complete specialization 
struct test<int> { // for the case T is int
    T* ptr;
};

One might ask why is this useful and indeed: That really looks useless. But take in mind that for example std::vector<bool>the referencetype looks completely different than for other Ts. Admittedly it doesn't change the kind of referencefrom a type to something different but nevertheless it could happen.

有人可能会问为什么这很有用,事实上:这看起来真的没用。不过,要记住,例如std::vector<bool>reference类型看起来比其他完全不同T秒。不可否认,它不会将类型reference从类型更改为不同的类型,但它仍然可能发生。

Now what happens if you write your own templates using this testtemplate. Something like this

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

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

it seems to be ok for you because you expectthat test<T>::ptris a type. But the compiler doesn't know and in deed he is even advised by the standard to expect the opposite, test<T>::ptrisn't a type. To tell the compiler what you expect you have to add a typenamebefore. The correct template looks like this

对您来说似乎没问题,因为您希望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;
}

Bottom line: You have to add typenamebefore whenever you use a nested type of a template in your templates. (Of course only if a template parameter of your template is used for that inner template.)

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

回答by Gupta

I think all of the answers have mentioned that the typenamekeyword, is used in two different cases:

我认为所有答案都提到了typename关键字,用于两种不同的情况:

a) When declaring a template type parameter. e.g.

a) 声明模板类型参数时。例如

template<class T> class MyClass{};        // these two cases are
template<typename T> class MyNewClass{};  // exactly the same.

Which there is no difference between them and they are EXACTLY the same.

它们之间没有区别,它们完全相同。

b) Before using a nested dependent type namefor a template.

b) 在为模板使用嵌套依赖类型名称之前。

template<class T>
void foo(const T & param)
{
   typename T::NestedType * value; // we should use typename here
}

Which not using typenameleads to parsing/compilation errors.

哪个不使用typename会导致解析/编译错误。

What I want to add to the second case, as mentioned in Scot Meyers book Effective C++, is that there is an exception of using typenamebefore a nested dependant type name. The exception is that if you use the nested dependant type nameeither as a base classor in a member initialization list, you should not use typenamethere:

正如 Scot Meyers 的书Effective C++ 中提到的,我想对第二种情况添加的是,typename嵌套依赖类型 name之前使用 using 是一个例外。例外情况是,如果您将嵌套的依赖类型名称用作基类成员初始化列表,则不应typename在那里使用:

template<class T>
class D : public B<T>::NestedType               // No need for typename here
{
public:
   D(std::string str) : B<T>::NestedType(str)   // No need for typename here
   {
      typename B<T>::AnotherNestedType * x;     // typename is needed here
   }
}

Note:Using typenamefor the second case (i.e. before nested dependent type name) is not needed since C++20.

注意:使用typename用于所述第二情况下(即前嵌套依赖型名称)不因为C ++ 20需要的。

回答by Phillip Ngan

Two uses:

两种用途:

  1. As a templateargument keyword (instead of class)
  2. A typenamekeyword tells the compiler that an identifier is a type (rather than a static member variable)
  1. 作为template参数关键字(而不是class
  2. typename关键字告诉编译器的标识符是一种类型的(而不是静态成员变量)
template <typename T> class X  // [1]
{
    typename T::Y _member;  // [2] 
}
template <typename T> class X  // [1]
{
    typename T::Y _member;  // [2] 
}

回答by Jobin

#include <iostream>

class A {
public:
    typedef int my_t;
};

template <class T>
class B {
public:
    // T::my_t *ptr; // It will produce compilation error
    typename T::my_t *ptr; // It will output 5
};

int main() {
    B<A> b;
    int my_int = 5;
    b.ptr = &my_int;
    std::cout << *b.ptr;
    std::cin.ignore();
    return 0;
}