关于c ++:什么时候需要“typename”关键字?

When is the “typename” keyword necessary?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Officially, what is typename for?
Where and why do I have to put the template and typename keywords?

考虑下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
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();
}

为什么"类型".在这个例子中,有必要吗?是否有任何其他的情况下,必须指定的"类型"?


简短回答:每当引用依赖名称的嵌套名称时,即嵌套在具有未知参数的模板实例中。

答案是:C++中有三层实体:值、类型和模板。所有这些都可以有名称,而单独的名称并不能告诉您它是实体的哪一层。相反,必须从上下文推断有关名称实体性质的信息。

如果不可能进行这种推断,则必须说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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::gnarlMagic::brughMagic::kwpq必须解释,因为这是不可能的:因为Magic是一个模板,所以类型Magic的本质取决于T,例如,可能存在与主模板完全不同的专门化。

使Magic::gnarl成为从属名称的原因是我们在一个模板定义中,其中T未知。如果我们使用Magic,这会有所不同,因为编译器知道(你保证!)Magic的完整定义。

(如果你想亲自测试,这里有一个你可以使用的Magic的示例定义)。请原谅在专门化中使用constexpr以实现简洁性;如果您有一个旧的编译器,请随意将静态成员常量声明更改为旧样式的pre-c++11格式。)

1
2
3
4
5
6
7
8
9
10
11
12
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; }
};

用途:

1
2
3
4
5
6
7
int main()
{
  A<int> a;
  a.foo();

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


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

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


只要类型名依赖于模板参数,就需要type name关键字(这样编译器就可以"知道"标识符(类型或值)的语义,而不必在第一次传递时使用完整的符号表)。

在使用通用模板参数(http://ideone.com/amimx)时,lone typename关键字的含义不同,也不太常见。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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();
}