2011-10-27 110 views
43

可能重复:
Officially, what is typename for?
Where and why do I have to put the template and typename keywords?何时需要“typename”关键字?

考虑下面的代码:

template<class K> 
class C { 
    struct P {}; 
    vector<P> vec; 
    void f(); 
}; 

template<class K> void C<K>::f() { 
    typename vector<P>::iterator p = vec.begin(); 
} 

为什么在这个例子中, “类型名” 必要的关键字? 是否有其他情况下必须指定“typename”?

+0

http://stackoverflow.com/questions/610245/where-and-why-do-我必须把模板和类型名称的依赖名称 –

回答

58

简短回答:每次引用的嵌套名称是从属名称,即嵌套在具有未知参数的模板实例中。

长答案:C++中有三层实体:值,类型和模板。所有这些都可以有名称,并且名称本身并不会告诉你它是哪一层实体。相反,关于名称实体性质的信息必须从上下文中推断出来。

每当这个推断是不可能的,你必须指定它:

template <typename> struct Magic; // defined somewhere else 

template <typename T> struct A 
{ 
    static const int value = Magic<T>::gnarl; // assumed "value" 

    typedef typename Magic<T>::brugh my_type; // decreed "type" 
    //  ^^^^^^^^ 

    void foo() { 
    Magic<T>::template kwpq<T>(1, 'a', .5); // decreed "template" 
    //  ^^^^^^^^ 
    } 
}; 

这里的名字Magic<T>::gnarlMagic<T>::brughMagic<T>::kwpq不得不被expliciated,因为它是不可能告诉:由于Magic是一个模板时,非常性质类型Magic<T>取决于T - 例如,可能存在与主模板完全不同的专业化。

什么令Magic<T>::gnarl成为独立名称是因为我们在模板定义中,其中T未知。如果我们使用了Magic<int>,这将是不同的,因为编译器知道(你保证!)Magic<int>的完整定义。如果你想自己测试一下,你可以使用Magic的一个示例定义,为了简洁起见,请特别注意在专门化中使用constexpr;如果你有一个老的编译器,可以随意更改静态成员常量声明旧式预C++ 11形式)

template <typename T> struct Magic 
{ 
    static const T     gnarl; 
    typedef T &      brugh; 
    template <typename S> static void kwpq(int, char, double) { T x; } 
}; 
template <> struct Magic<signed char> 
{ 
    // note that `gnarl` is absent 
    static constexpr long double brugh = 0.25; // `brugh` is now a value 
    template <typename S> static int kwpq(int a, int b) { return a + b; } 
}; 

用法:

int main() 
{ 
    A<int> a; 
    a.foo(); 

    return Magic<signed char>::kwpq<float>(2, 3); // no disambiguation here! 
} 
+0

@Nils:'const'不是有效的C++。 –

+0

感谢您的解释。但我无法理解最后一件事:由于名称'Magic :: kwpq'依赖于模板参数'T',这将在*两阶段查找的第二阶段中解决*,对吗?那么为什么编译器当时不能检查(当他能够确定'kwpq'是一个模板还是一个类型或者其他什么的时候),如果这个名字根据它的*层实体*正确使用的话? –

+0

@PaoloM:类模板“A”的成员函数的定义必须在第一阶段解析,因此您需要对语法进行消歧才有意义。在第二阶段,我们检查实例'Magic '为具体类型'T'实际上有一个'kwpq'成员,它是具有适当签名的函数。 (并感谢编辑!) –

7

需要typename关键字每当类型名称取决于模板参数,(所以编译器可以“知道”的标识符()的语义,而不在所述第一具有全符号表通过)。


不在同一个意思,有点不太常见,孤独typename关键字也可以使用通用的模板参数时是有用的:http://ideone.com/amImX

#include <string> 
#include <list> 
#include <vector> 

template <template <typename, typename> class Container, 
      template <typename> class Alloc = std::allocator> 
struct ContainerTests 
{ 
    typedef Container<int, Alloc<int> > IntContainer; 
    typedef Container<std::string, Alloc<int> > StringContainer; 
    // 
    void DoTests() 
    { 
     IntContainer ints; 
     StringContainer strings; 
     // ... etc 
    } 
}; 

int main() 
{ 
    ContainerTests<std::vector> t1; 
    ContainerTests<std::list> t2; 

    t1.DoTests(); 
    t2.DoTests(); 
} 
+0

我从来不知道ab出来“孤独的类型名称”之前,这很酷! –

10

typename关键字,是必要的,因为iteratorP上的依赖类型。编译器无法猜测iterator是否指向一个值或一个类型,所以它会假设它的值,除非你叫typename。只要有一个依赖于模板参数的类型,就需要它,在类型或值都是有效的上下文中。例如,由于基类必须是类型,因此不需要基类typename

在同一主题上有一个template关键字,用于让编译器知道某些从属名称是模板函数而不是值。

相关问题