首页 文章

什么时候需要“typename”关键字? [重复]

提问于
浏览
46

可能重复:正式,什么是typename?我必须在哪里以及为什么要放置模板和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”关键字?是否还有其他必须指定“typename”的情况?

3 回答

  • 64

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

    答案很长: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 必须被解释,因为无法判断:由于 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!
    }
    
  • 7

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

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

  • 10

    每当类型名称依赖于模板参数时,都需要typename关键字(因此编译器可以_1119525_标识符(类型或值)的语义,而在第一次传递时没有完整的符号表) .


    在使用通用模板参数时,单独的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();
    }
    

相关问题