你为什么有时需要写'typename T`而不只是`T`?

时间:2009-08-08 00:13:10

标签: c++ sfinae

我正在阅读SFINAE上的维基百科文章,并遇到以下代码示例:

struct Test 
{
    typedef int Type;
};

template < typename T > 
void f( typename T::Type ) {} // definition #1

template < typename T > 
void f( T ) {}                // definition #2

void foo()
{
    f< Test > ( 10 ); //call #1 

    f< int > ( 10 );  //call #2 without error thanks to SFINAE
}

现在我实际上已经编写了这样的代码,并且在某种程度上直觉上我知道我需要键入“typename T”而不是“T”。但是,了解它背后的实际逻辑会很高兴。有人在乎解释吗?

3 个答案:

答案 0 :(得分:13)

每当X为或依赖于模板参数时,您需要执行typename X::Y的简短版本。在X已知之前,编译器无法判断Y是类型还是值。因此,您必须添加typename以指定它是一种类型。

例如:

template <typename T>
struct Foo {
  typename T::some_type x; // T is a template parameter. `some_type` may or may not exist depending on what type T is.
};

template <typename T>
struct Foo {
  typename some_template<T>::some_type x; // `some_template` may or may not have a `some_type` member, depending on which specialization is used when it is instantiated for type `T`
};

正如sbi在评论中指出的那样,歧义的原因是Y可能是静态成员,枚举或函数。在不知道X的类型的情况下,我们无法分辨。 该标准指定编译器应假定它是一个值,除非使用typename关键字明确标记为类型。

听起来评论者真的希望我提及另一个相关案例:;)

如果依赖名称是函数成员模板,并且您使用显式模板参数(例如foo.bar<int>())调用它,则必须在函数名称前添加template关键字,如在foo.template bar<int>()

原因是如果没有template关键字,编译器会认为bar是一个值,并且您希望在其上调用小于运算符(operator<)。

答案 1 :(得分:8)

一般来说,C ++的语法(继承自C)有一个技术缺陷:解析器必须知道某个东西是否命名一个类型,否则它只是无法解决某些歧义(例如,X * Y a乘法,或指针Y到X类型对象的声明?这一切都取决于X是否命名一个类型......! - )。 typename“形容词”可让您在需要时完全清晰明确(正如另一个答案所提到的那样,当涉及模板参数时,这是典型的;)。

答案 2 :(得分:3)

基本上,在编写模板代码时需要typename关键字(即,您在函数模板或类模板中)并且您指的是依赖于可能未知的模板参数的标识符是一种类型,但必须在模板代码中解释为一种类型。

在您的示例中,您在定义#1使用typename T::Type,因为T::Type取决于模板参数T,否则可能是数据成员。

在定义#2中您不需要typename T,因为T被声明为类型作为模板定义的一部分。