Answers:
以下是Josuttis书中的引文:
typename
引入关键字是为了指定后面的标识符是一种类型。考虑以下示例:template <class T> Class MyClass { typename T::SubType * ptr; ... };
在这里,
typename
用于说明SubType
是的类型class T
。因此,ptr
是指向type的指针T::SubType
。没有typename
,SubType
将被视为静态成员。从而T::SubType * ptr
将是
SubType
type 的值T
与的乘积ptr
。
Stroustrup 重用了现有的class关键字来指定类型参数,而不是引入可能会破坏现有程序的新关键字。不是没有考虑一个新的关键字-只是考虑到它的潜在破坏性,它不被认为是必需的。而直到ISO-C ++标准,这是声明的类型参数的唯一途径。
因此基本上Stroustrup重用了class关键字,而没有引入一个新关键字,该关键字随后在标准中由于以下原因而更改
作为给出的例子
template <class T>
class Demonstration {
public:
void method() {
T::A *aObj; // oops …
// …
};
语言语法误解T::A *aObj;
为算术表达式,因此引入了一个新关键字,称为typename
typename T::A* a6;
它指示编译器将后续语句视为声明。
由于关键字在工资单上,所以为什么不解决由最初决定重用class关键字引起的混乱。
这就是为什么我们都
您可以看一下这篇文章,它一定会对您有帮助,我只是从中提取了尽可能多的内容
typename
如果您可以将现有关键字class
用于相同目的,那么为什么需要一个新关键字?
typename
如Naveen的答案中所述,通过引用Josuttis,有必要解决解析问题。(我认为class
在此位置插入a 不会起作用。)只有在这种情况下接受了new关键字之后,模板参数声明(还是该定义?)中才允许使用它,因为class
始终存在一些误导。
在某些情况下,当您引用所谓的依赖类型的成员(意思是“依赖于模板参数”)时,编译器不能总是明确地推断出结果构造的语义,因为它不知道那是什么类型的名称。 (即,它是类型的名称,数据成员的名称还是其他名称)。在这种情况下,您必须通过明确告诉编译器该名称属于定义为该依赖类型的成员的类型名称来消除歧义。
例如
template <class T> struct S {
typename T::type i;
};
在此示例中,关键字typename
对于代码进行编译是必需的。
当您要引用依赖类型的模板成员(即,指定模板的名称)时,会发生相同的事情。您还必须使用关键字来帮助编译器template
,尽管它的放置位置不同
template <class T> struct S {
T::template ptr<int> p;
};
在某些情况下,可能需要同时使用
template <class T> struct S {
typename T::template ptr<int>::type i;
};
(如果语法正确)。
当然,关键字的另一个作用typename
是在模板参数声明中使用。
秘密在于,模板可以专门用于某些类型。这意味着它还可以为几种类型定义完全不同的接口。例如,您可以编写:
template<typename T>
struct test {
typedef T* ptr;
};
template<> // complete specialization
struct test<int> { // for the case T is int
T* ptr;
};
有人可能会问,为什么这样做有用,并且确实如此:看起来真的没用。但是请记住,例如std::vector<bool>
,reference
类型看起来与其他类型完全不同T
。诚然,它不会将类型reference
从类型更改为其他内容,但是仍然可能发生。
现在,如果您使用此test
模板编写自己的模板,将会发生什么。像这样
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
您似乎还可以,因为您希望这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;
}
底线:typename
每当您在模板中使用模板的嵌套类型时,都必须在添加之前。(当然,仅当您的内部模板使用模板的模板参数时。)
我认为所有答案都提到typename
在两种不同情况下使用了关键字:
a)声明模板类型参数时。例如
template<class T> class MyClass{}; // these two cases are
template<typename T> class MyNewClass{}; // exactly the same.
它们之间没有区别,它们完全相同。
b)在为模板使用嵌套的依赖类型名称之前。
template<class T>
void foo(const T & param)
{
typename T::NestedType * value; // we should use typename here
}
不使用typename
会导致解析/编译错误。
正如Scot Meyers的书《Effective C ++》中提到的,我想添加到第二种情况的是,typename
在嵌套的依赖类型名称之前使用例外。例外是,如果您将嵌套的依赖类型名称用作基类或在成员初始化列表中,则不应在其中使用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
}
}
注意:使用typename
用于所述第二情况下(即前嵌套依赖型名称)不因为C ++ 20需要的。
#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;
}