10

我正在阅读有关SFINAE的 Wikipedia 文章并遇到以下代码示例:

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”。但是,很高兴知道它背后的实际逻辑。有人愿意解释吗?

4

3 回答 3

14

typename X::Y每当 X 是或取决于模板参数时,您需要做的简短版本。在知道 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>().

这样做的原因是没有模板关键字,编译器假定它bar是一个值,并且您希望在其上调用小于运算符 ( operator<)。

于 2009-08-08T00:15:48.020 回答
10

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

于 2009-08-08T00:18:58.723 回答
3

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

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

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

于 2009-08-08T00:19:43.793 回答