【问题标题】:C++ template gotchasC++ 模板陷阱
【发布时间】:2009-12-09 23:26:58
【问题描述】:

刚才我不得不在网站上挖掘一下,为什么模板类模板成员函数会出现语法错误:

template<class C> class F00 {
   template<typename T> bar();
};
...
Foo<C> f;
f.bar<T>(); // syntax error here

我现在意识到模板括号被视为关系运算符。要执行预期的操作,需要以下奇怪的语法,参见Templates: template function not playing well with class's template member function

f.template bar<T>();

您还遇到过哪些 C++/C++ 模板的奇怪方面和陷阱,您认为这些不是常识?

【问题讨论】:

  • 顺便说一句,您可能只想将其限制为模板陷阱,对于一般 C++ 陷阱还有另一个问题。
  • template&lt;typename T&gt; bar(); 有什么返回类型?
  • 你的假设是错误的。您在这里不需要template,因为f 不是从属名称。无论如何,它取决于什么?对于Foo&lt;C&gt; 类型的对象,它是一个未指定范围内的名称。

标签: c++ templates


【解决方案1】:

我第一次从另一个模板类继承模板类时被绊倒了:

template<typename T>
class Base {
    int a;
};

template<typename T>
class Derived : public Base<T> {
    void func() {
        a++; // error! 'a' has not been declared
    }
};

问题在于编译器不知道Base&lt;T&gt; 是默认模板还是专用模板。专用版本可能没有 int a 作为成员,因此编译器不会假定它可用。但是你可以通过using 指令告诉编译器它会在那里:

template<typename T>
class Derived : public Base<T> {
    using Base<T>::a;
    void func() {
        a++; // OK!
    }
};

或者,您可以明确表明您正在使用T 的成员:

void func {
    T::a++; // OK!
}

【讨论】:

  • “编译器不知道 Base 是否会成为默认模板”:这是一个很好的解释。请注意,使用基类成员变量实际上会破坏封装,因此不鼓励。
  • 基类方法也存在同样的问题。问题发生在名称查找中,此时您甚至不知道会找到什么。
【解决方案2】:

这件事让我当时很沮丧:

#include <vector>
using std::vector;

struct foo {
  template<typename U>
  void vector();
};

int main() {
  foo f;
  f.vector<int>(); // ambiguous!
}

main 中的最后一行是模棱两可的,因为编译器不仅在 foo 内查找 vector,而且还在 main 内查找为非限定名称。所以它找到了std::vectorfoo::vector。要解决此问题,您必须编写

f.foo::vector<int>();

GCC 不关心这一点,并通过做直观的事情(调用成员)来接受上面的代码,其他编译器做得更好并像 comeau 一样发出警告:

"ComeauTest.c", line 13: warning: ambiguous class member reference -- function
          template "foo::vector" (declared at line 8) used in preference to
          class template "std::vector" (declared at line 163 of
          "stl_vector.h")
        f.vector<int>(); // ambiguous!

【讨论】:

  • 是否有 GCC 标志让它对此发出警告? (我敢打赌,大约有一百万个......)
  • 嗯?我不明白这对编译器来说是多么模棱两可。 f. 之后的任何东西都必须是foo 的成员,那为什么它会找到std::vector?在任何情况下您都希望它找到非会员?
  • @Peter 这与神秘的语言情况有关。在 C++0x 中不再存在 vector 在周围范围内查找的规则。它还在现代 C++03 编译器上只产生警告(如果有的话)。例如,see here for clang。在f. 之后,下一个名称不一定是foo 的成员。例如考虑:struct A { void f(); }; struct B { typedef A type; }; int main() { A a; a.B::type::f(); }.
【解决方案3】:

SO 上关于模板的问题之星:缺少的类型名!

template <typename T>
class vector
{
  public:
    typedef T * iterator;
    ...
};

template <typename T>
void func()
{
    vector<T>::iterator it;           // this is not correct!

    typename vector<T>::iterator it2; // this is correct.
}

这里的问题是vector&lt;T&gt;::iterator 是一个依赖名称:它依赖于一个模板参数。结果,编译器不知道iterator 指定了一个类型;我们需要用typename 关键字告诉他。

模板内部类或模板成员/静态函数也是如此:必须使用 template 关键字消除歧义,如 OP 中所述。

template <typename T>
void func()
{
    T::staticTemplateFunc<int>();          // ambiguous

    T::template staticTemplateFunc<int>(); // ok

    T t;

    t.memberTemplateFunc<int>();          // ambiguous

    t.template memberTemplateFunc<int>(); // ok
}

【讨论】:

    【解决方案4】:

    超出范围的类成员函数定义:

    template <typename T> 
    class List {                     // a namespace scope class template 
      public: 
        template <typename T2>       // a member function template 
        List (List<T2> const&);      // (constructor) 
        … 
    }; 
    template <typename T> 
     template <typename T2> 
    List<T>::List (List<T2> const& b) // an out-of-class member function 
    {                                 // template definition 
        … 
    } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多