views:

447

answers:

4

just now I had to dig through the website to find out why template class template member function was giving syntax errors:

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

I now realize that template brackets are treated as relational operators. To do what was intended the following bizarre syntax is needed, cf http://stackoverflow.com/questions/1682844/templates-template-function-not-playing-well-with-classs-template-member-functi:

f.template bar<T>();

what other bizarre aspects and gotcha of C++/C++ templates you have encountered that were not something that you would consider to be common knowledge?

+6  A: 

This one got me upset back then:

#include <vector>
using std::vector;

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

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

The last line in main is ambiguous, because the compiler not only looks up vector within foo, but also as an unqualified name starting from within main. So it finds both std::vector and foo::vector. To fix this, you have to write

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

GCC does not care about that, and accepts the above code by doing the intuitive thing (calling the member), other compilers do better and warn like 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!
Johannes Schaub - litb
Is there a GCC flag to make it warn about this? (I bet there is, there are about a million of them...)
Chris Lutz
+6  A: 

I got tripped up the first time I inherited a templated class from another templated class:

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

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

The problem is that the compiler doesn't know if Base<T> is going to be the default template or a specialized one. A specialized version may not have int a as a member, so the compiler doesn't assume that it's available. But you can tell the compiler that it's going to be there with the using directive:

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

Alternatively, you can make it explicit that you are using a member of T:

void func {
    T::a++; // OK!
}
int3
"the compiler doesn't know if Base<T> is going to be the default template": that's a great explanation. Note that using a base class member variable is _actually_ breaking encapsulation, hence discouraged.
xtofl
The same problem exists for base class _methods_. The problem happens in name lookup, at which point you don't even know what you'll find.
MSalters
+2  A: 

Out of scope class member function definition:

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 
 … 
}
navigator
+2  A: 

The star of questions about templates here on SO: the missing typename!

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.
}

The problem here is that vector<T>::iterator is a dependent name: it depends on a template parameter. As a consequence, the compiler does not know that iterator designates a type; we need to tell him with the typename keyword.

The same goes for template inner classes or template member/static functions: they must be disambiguated using the template keyword, as noted in the 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
}
Luc Touraille