Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloading Output operator for a class template in a namespace

I've this program

#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
#include <algorithm>
using namespace std ;

#if 0
namespace skg 
{
 template <class T>
  struct Triplet ;
}

template <class T>
ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t) ;
#endif

namespace skg
{
 template <class T>
  struct Triplet
  {
 //  friend ostream& ::operator<< <> (ostream& os, const Triplet<T>& p_t) ;

   private:
   T x, y, z ;

   public:
   Triplet (const T& p_x, const T& p_y, const T& p_z)
    : x(p_x), y(p_y), z(p_z) { }
  } ;
}

template <class T>
ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t)
{
 os << '(' << p_t.x << ',' << p_t.y << ',' << p_t.z << ')' ;
 return os ;
}

namespace {
 void printVector()
 {
  typedef skg::Triplet<int> IntTriplet ;

  vector< IntTriplet > vti ;
  vti.push_back (IntTriplet (1, 2, 3)) ;
  vti.push_back (IntTriplet (5, 5, 66)) ;

  copy (vti.begin(), vti.end(), ostream_iterator<IntTriplet> (cout, "\n")) ;
 }
}
int main (void)
{
 printVector() ;
}

Compilation fails because compiler could not find any output operator for skg::Triplet. But output operator does exist.

If I move Triplet from skg namespace to global namespace everything works fine. what is wrong here ?

like image 788
Surya Avatar asked Jan 29 '10 03:01

Surya


1 Answers

You need to move your implementation of operator<< into the same namespace as your class. It's looking for:

ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t)

But won't find it because of a short-coming in argument-dependent look-up (ADL). ADL means that when you call a free function, it'll look for that function in the namespaces of it's arguments. This is the same reason we can do:

std::cout << "Hello" << std::endl;

Even though operator<<(std::ostream&, const char*) is in the std namespace. For your call, those namespaces are std and skg.

It's going to look in both, not find one in skg (since yours is in the global scope), then look in std. It will see possibilities (all the normal operator<<'s), but none of those match. Because the code running (the code in ostream_iterator) is in the namespace std, access to the global namespace is completely gone.

By placing your operator in the same namespace, ADL works. This is discussed in an article by Herb Sutter: "A Modest Proposal: Fixing ADL.". (PDF). In fact, here's a snippet from the article (demonstrating a shortcoming):

// Example 2.4
//
// In some library header:
//
namespace N { class C {}; }
int operator+( int i, N::C ) { return i+1; }

// A mainline to exercise it:
//
#include <numeric>
int main() {
    N::C a[10];
    std::accumulate( a, a+10, 0 ); // legal? not specified by the standard
}

Same situation you have.

The book "C++ Coding Standards" by Sutter and & Alexandrescu has a useful guideline:

  1. Keep a type and its nonmember function interface in the same namespace.

Follow it and you and ADL will be happy. I recommend this book, and even if you can't get one at least read the PDF I linked above; it contains the relevant information you should need.


Note that after you move the operator, you'll need your friend directive (so you can access private variables):

template <typename U>
friend ostream& operator<< (ostream& os, const Triplet<U>& p_t);

And ta-da! Fixed.

like image 86
GManNickG Avatar answered Sep 21 '22 08:09

GManNickG