【问题标题】:When is the "typename" keyword necessary? [duplicate]什么时候需要“typename”关键字? [复制]
【发布时间】:2011-12-16 22:13:10
【问题描述】:

可能重复:
Officially, what is typename for?
Where and why do I have to put the template and typename keywords?

考虑下面的代码:

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”关键字? 是否还有其他必须指定“typename”的情况?

【问题讨论】:

标签: c++ templates syntax typename


【解决方案1】:

简答:每当引用作为从属名称的嵌套名称时,即嵌套在具有未知参数的模板实例中。

长答案: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&lt;T&gt;::gnarlMagic&lt;T&gt;::brughMagic&lt;T&gt;::kwpq 必须明确说明,因为无法分辨:由于Magic 是一个模板,该类型的性质 Magic&lt;T&gt; 依赖于 T -- 例如,可能存在与主模板完全不同的特化。

使Magic&lt;T&gt;::gnarl 成为依赖名称的原因是我们位于模板定义中,而T 是未知的。如果我们使用Magic&lt;int&gt;,情况会有所不同,因为编译器知道(你保证!)Magic&lt;int&gt; 的完整定义。

(如果你想自己测试,这里有一个 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!
}

【讨论】:

  • @Nils: const 不是有效的 C++。
  • 感谢您的解释。但我无法理解最后一件事:由于名称Magic&lt;T&gt;::kwpq 依赖于模板参数T,这将在两阶段查找 的第二阶段解决,对吧?那么为什么编译器不能在那个时候检查(当他能够确定 kwpq 是模板还是类型或其他什么时)如果根据其 实体层正确使用了名称?
  • @PaoloM:类模板A的成员函数的定义必须在第一阶段已经解析,因此您需要消除歧义以使语法有意义。在第二阶段,我们检查具体类型T 的实例化Magic&lt;T&gt; 实际上有一个成员kwpq,它是一个具有适当签名的函数。 (感谢您的编辑!)
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    只要类型名称依赖于模板参数,就需要 typename 关键字,(因此编译器可以“知道”标识符的语义(typevalue)在第一次传递时没有完整的符号表)。


    含义不同,也不太常见,单独的 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();
    }
    

    【讨论】:

    • 我以前从不知道“lone typename”,这很酷!
    猜你喜欢
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    • 2011-10-16
    • 1970-01-01
    • 2012-09-16
    • 2021-07-25
    • 2012-08-23
    相关资源
    最近更新 更多