Template Class in C++ FAQ part 2

[35.16] Why do I get linker errors when I use template friends?

Ah, the intricacies of template friends. Here’s an example of what people often want to do:

 #include <iostream>
 
 template<typename T>
 class Foo {
 public:
   Foo(const T& value = T());
   friend Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs);
   friend std::ostream& operator<< (std::ostream& o, const Foo<T>& x);
 private:
   T value_;
 };

Naturally the template will need to actually be used somewhere:

 int main()
 {
   Foo<int> lhs(1);
   Foo<int> rhs(2);
   Foo<int> result = lhs + rhs;
   std::cout << result;
   

 }

And of course the various member and friend functions will need to be defined somewhere:

 template<typename T>
 Foo<T>::Foo(const T& value = T())
   : value_(value)
 { }
 
 template<typename T>
 Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs)
 { return Foo<T>(lhs.value_ + rhs.value_); }
 
 template<typename T>
 std::ostream& operator<< (std::ostream& o, const Foo<T>& x)
 { return o << x.value_; }

The snag happens when the compiler sees the friend lines way up in the class definition proper. At that moment it does not yet know the friend functions are themselves templates; it assumes they are non-templates like this:

 Foo<int> operator+ (const Foo<int>& lhs, const Foo<int>& rhs)
 { ... }
 
 std::ostream& operator<< (std::ostream& o, const Foo<int>& x)
 { ... }

When you call the operator+ or operator<< functions, this assumption causes the compiler to generate a call to the non-template functions, but the linker will give you an "undefined external" error because you never actually defined those non-template functions.

The solution is to convince the compiler while it is examining the class body proper that the operator+ and operator<< functions are themselves templates. There are several ways to do this; one simple approach is pre-declare each template friend function above the definition of template class Foo:

 template<typename T> class Foo;  // pre-declare the template class itself
 template<typename T> Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs);
 template<typename T> std::ostream& operator<< (std::ostream& o, const Foo<T>& x);

Also you add <> in the friend lines, as shown:

 #include <iostream>
 
 template<typename T>
 class Foo {
 public:
   Foo(const T& value = T());
   friend Foo<T> operator+ <> (const Foo<T>& lhs, const Foo<T>& rhs);
   friend std::ostream& operator<< <> (std::ostream& o, const Foo<T>& x);
 private:
   T value_;
 };

After the compiler sees that magic stuff, it will be better informed about the friend functions. In particular, it will realize that the friend lines are referring to functions that are themselves templates. That eliminates the confusion.

Another approach is to define the friend function within the class body at the same moment you declare it to be a friend. For example:

 #include <iostream>
 
 template<typename T>
 class Foo {
 public:
   Foo(const T& value = T());
 
   friend Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs)
   {
     

   }
 
   friend std::ostream& operator<< (std::ostream& o, const Foo<T>& x)
   {
     

   }
 
 private:
   T value_;
 };

[35.17] How can any human hope to understand these overly verbose template-based error messages?

Here’s a free tool that transforms error messages into something more understandable. At the time of this writing, it works with the following compilers: Comeau C++, Intel C++, CodeWarrior C++, gcc, Borland C++, Microsoft Visual C++, and EDG C++.

Here’s an example showing some unfiltered gcc error messages:

 rtmap.cpp: In function `int main()':
 rtmap.cpp:19: invalid conversion from `int' to `
    std::_Rb_tree_node<std::pair<const int, double> >*'
 rtmap.cpp:19:   initializing argument 1 of `std::_Rb_tree_iterator<_Val, _Ref,
    _Ptr>::_Rb_tree_iterator(std::_Rb_tree_node<_Val>*) [with _Val =
    std::pair<const int, double>, _Ref = std::pair<const int, double>&, _Ptr =
    std::pair<const int, double>*]'
 rtmap.cpp:20: invalid conversion from `int' to `
    std::_Rb_tree_node<std::pair<const int, double> >*'
 rtmap.cpp:20:   initializing argument 1 of `std::_Rb_tree_iterator<_Val, _Ref,
    _Ptr>::_Rb_tree_iterator(std::_Rb_tree_node<_Val>*) [with _Val =
    std::pair<const int, double>, _Ref = std::pair<const int, double>&, _Ptr =
    std::pair<const int, double>*]'
 E:/GCC3/include/c++/3.2/bits/stl_tree.h: In member function `void
    std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::insert_unique(_II,
     _II) [with _InputIterator = int, _Key = int, _Val = std::pair<const int,
    double>, _KeyOfValue = std::_Select1st<std::pair<const int, double> >,
    _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int,
    double> >]':
 E:/GCC3/include/c++/3.2/bits/stl_map.h:272:   instantiated from `void std::map<_
 Key, _Tp, _Compare, _Alloc>::insert(_InputIterator, _InputIterator) [with _Input
 Iterator = int, _Key = int, _Tp = double, _Compare = std::less<int>, _Alloc = st
 d::allocator<std::pair<const int, double> >]'
 rtmap.cpp:21:   instantiated from here
 E:/GCC3/include/c++/3.2/bits/stl_tree.h:1161: invalid type argument of `unary *
    '

Here’s what the filtered error messages look like (note: you can configure the tool so it shows more information; this output was generated with settings to strip things down to a minimum):

 rtmap.cpp: In function `int main()':
 rtmap.cpp:19: invalid conversion from `int' to `iter'
 rtmap.cpp:19:   initializing argument 1 of `iter(iter)'
 rtmap.cpp:20: invalid conversion from `int' to `iter'
 rtmap.cpp:20:   initializing argument 1 of `iter(iter)'
 stl_tree.h: In member function `void map<int,double>::insert_unique(_II, _II)':
     [STL Decryptor: Suppressed 1 more STL standard header message]
 rtmap.cpp:21:   instantiated from here
 stl_tree.h:1161: invalid type argument of `unary *'

Here is the source code to generate the above example:

 #include <map>
 #include <algorithm>
 #include <cmath>
 
 const int values[] = { 1,2,3,4,5 };
 const int NVALS = sizeof values / sizeof (int);
 
 int main()
 {
     using namespace std;
 
     typedef map<int, double> valmap;
 
     valmap m;
 
     for (int i = 0; i < NVALS; i++)
         m.insert(make_pair(values[i], pow(values[i], .5)));
 
     valmap::iterator it = 100;              
// error
     valmap::iterator it2(100);              
// error
     m.insert(1,2);                          
// error
 
     return 0;
 }

[35.18] Why am I getting errors when my template-derived-class uses a nested type it inherits from its template-base-class?  New! 

[Recently created thanks to Victor Bazarov (in 9/06). Click here to go to the next FAQ in the "chain" of recent changes.]

Perhaps surprisingly, the following code is not valid C++, even though some compilers accept it:

 template<typename T>
 class B {
 public:
   class Xyz { ... };  
 type nested in class B<T>
   typedef int Pqr;    
 type nested in class B<T>
 };
 
 template<typename T>
 class D : public B<T> {
 public:
   void g()
   {
     Xyz x;  
 bad (even though some compilers erroneously (temporarily?) accept it)
     Pqr y;  
 bad (even though some compilers erroneously (temporarily?) accept it)
   }
 };

This might hurt your head; better if you sit down.

Within D<T>::g(), name Xyz and Pqr do not depend on template parameter T, so they are known as a nondependent names. On the other hand, B<T> is dependent on template parameter T so B<T> is called a dependent name.

Here’s the rule: the compiler does not look in dependent base classes (like B<T>) when looking up nondependent names (like Xyz or Pqr). As a result, the compiler does not know they even exist let alone are types.

At this point, programmers sometimes prefix them with B<T>::, such as:

 template<typename T>
 class D : public B<T> {
 public:
   void g()
   {
     B<T>::Xyz x;  
 bad (even though some compilers erroneously (temporarily?) accept it)
     B<T>::Pqr y;  
 bad (even though some compilers erroneously (temporarily?) accept it)
   }
 };

Unfortunately this doesn’t work either because those names (are you ready? are you sitting down?) are not necessarily types. "Huh?!?" you say. "Not types?!?" you exclaim. "That’s crazy; any fool can SEE they are types; just look!!!" you protest. Sorry, the fact is that they might not be types. The reason is that there can be a specialization of B<T>, say B<Foo>, where B<Foo>::Xyz is a data member, for example. Because of this potential specialization, the compiler cannot assume that B<T>::Xyz is a type until it knows T. The solution is to give the compiler a hint via the typename keyword:

 template<typename T>
 class D : public B<T> {
 public:
   void g()
   {
     typename B<T>::Xyz x;  
 good
     typename B<T>::Pqr y;  
 good
   }
 };

35.19] Why am I getting errors when my template-derived-class uses a member it inherits from its template-base-class?  Updated! 

[Recently wordsmithed, clarified (in 9/06). Click here to go to the next FAQ in the "chain" of recent changes.]

Perhaps surprisingly, the following code is not valid C++, even though some compilers accept it:

 template<typename T>
 class B {
 public:
   void f() { }  
 member of class B<T>
 };
 
 template<typename T>
 class D : public B<T> {
 public:
   void g()
   {
     f();  
 bad (even though some compilers erroneously (temporarily?) accept it)
   }
 };

This might hurt your head; better if you sit down.

Within D<T>::g(), the name f does not depend on template parameter T, so f is known as a nondependent name. On the other hand, B<T> is dependent on template parameter T so B<T> is called a dependent name.

Here’s the rule: the compiler does not look in dependent base classes (like B<T>) when looking up nondependent names (like f).

This doesn’t mean that inheritance doesn’t work. Class D<int> is still derived from class B<int>, the compiler still lets you implicitly do the is-a conversions (e.g., D<int>* to B<int>*), dynamic binding still works when virtual functions are invoked, etc. But there is an issue about how names are looked up.

Workarounds:

  • Change the call from f() to this->f(). Since this is always implicitly dependent in a template, this->f is dependent and the lookup is therefore deferred until the template is actually instantiated, at which point all base classes are considered.
  • Insert using B<T>::f; just prior to calling f().
  • Change the call from f() to B<T>::f(). Note however that this might not give you what you want if f() is virtual, since it inhibits the virtual dispatch mechanism.

[35.20] Can the previous problem hurt me silently? Is it possible that the compiler will silently generate the wrong code?  Updated! 

[Recently rewrote to include non-dependent type-names in addition to non-dependent member-names thanks to a discussion with Victor Bazarov (in 9/06). Click here to go to the next FAQ in the "chain" of recent changes.]

Yes.

Since non-dependent types and non-dependent members are not found in the dependent template base-classes, the compiler will search the enclosing scope, such as the enclosing namespace. This can cause it to silently(!) do the wrong thing.

For example:

 class Xyz { ... };   global ("namespace scope") type
 void f() { }        
 global ("namespace scope") function
 
 template<typename T>
 class B {
 public:
   class Xyz { ... };  
 type nested in class B<T>
   void f() { }        
 member of class B<T>
 };
 
 template<typename T>
 class D : public B<T> {
 public:
   void g()
   {
     Xyz x;  
 suprise: you get the global Xyz!!
     f();    
 suprise: you get the global f!!
   }
 };

The use of Xyz and f within D<T>::g() will silently(!) resolve to the global entities rather than those inherited from class B<T>.

You have been warned.

C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised

This entry was posted in Research related. Bookmark the permalink.

Leave a comment