0

I'm trying to get up to speed on "modern" C++ particularly using templates. I've got a class Point that overrides the << operator. Works fine. So then I decided to add nice print out for containers in general.

My question - is there a way write a template for multiple container types similar to the code below?

template <typename T>
std::ostream& operator <<(ostream& os, const vector<T>& v)
{
  os << "\n";
  for( auto x : v ) { os << "\n\t" << x; }
  os << "\n";
  return os;
}

The code above correctly outputs vectors in a multi-line format as long as T has an << operator. I'd like to make this work for other containers like lists etc.

I also realize that it's probably a bad idea (or at least rude) to override output for containers in a general way for all types. So ultimately the template code above would have the typename hard coded/restricted to 'Point' and a template-ed container.


OK, implimenting AndyG's suggestion I have the following full code:

#include <iostream>
#include <map>
#include <vector>

using namespace std;


struct Point {
  double x, y, z;

  Point() : x(0), y(0), z(0) {};
  Point(double a, double b, double c) : x(a), y(b), z(c) {}
  Point(double a[]) : x(a[0]), y(a[0]), z(a[0]) {}

  friend std::ostream& operator <<(ostream& os, const Point&p)
  {
    os << p.x << ", " << p.y << ", "<< p.z;
    return os;
  }

};

template <template<class> class C, class... T>
std::ostream& operator <<(ostream& os, const C<T...>& v)
{
  os << "\n";
  for( auto x : v ) { os << "\n\t" << x; }
  os << "\n";
  return os;
}

vector<Point> vp = { { 1, 2, 3 },
                     { 5, 7, 4 },
                     { 8, 2, 5 }
};

int main(int argc, char* argv[])
{
  cout << vp[0]; // works
  cout << vp;    // dosen't work,
}

But still no luck. The compiler can't match operator<< with 'std::vector'

I had tried many variations of the template <template> prior to my first post with about the same results. I can get a template that compiles but the compiler fails to match the operator function and use the template.

I wasn't aware of the variadic template pack, obviously a good idea but doesn't solve the problem.

DBB
  • 1
  • 3
  • `template – AndyG Jan 17 '17 at 18:29
  • 1
    A similar effort is described here: http://stackoverflow.com/questions/4850473/pretty-print-c-stl-containers – Kerrek SB Jan 17 '17 at 18:33
  • And, as you say, it is extremely rude to add a template that matches **all** T's, including my types. That's one reason why `std::vector` doesn't already have one. – Bo Persson Jan 17 '17 at 18:54
  • Yeah, this is really a rather lame use case and has issues. However what I'm really trying to do is understand what I do understand and what I don't understand. – DBB Jan 17 '17 at 19:09

1 Answers1

0

OK, AndyG set me on the right track and I think I understand what is going on.

The following code doesn't work because the std::container templates also have an allocator parameter which has a default and you rarely need to use it. Hence we think of most container templates of taking just the type/class that they will contain.

This

template < template <class> class C, class... T>
std::ostream& operator <<(ostream& os, const C<T...>& v)
{ ... }

doesn't work because the template we are going to call C in our operator<< function takes two parameters not one. Therefore the compiler won't find a match.

This will work:

template < template <class, class> class C, class... T>
std::ostream& operator <<(ostream& os, const C<T...>& v)

because the compiler can match vector<Point, Alloc> to template<class, class> which we choose to call C. It can then use our function template to generate an overload for operator <<.

Note that there are a number of things wrong with this approach in general. The template <class, class> isn't going to match a std::map which takes 4 parameters, 2 of which are defaulted. Even worse it will match any two argument template that may or may not be a container.

We can fix this by using a varg parameters: template <class, class...> however we are still screwed because now the << function template is dealing with std::pairs from the iterator which the compiler doesn't know how to cout <<.

So while this was a useful exercise for me to understand templates better, don't do it.

This link has a neat container pretty print lib: Pretty-print C++ STL containers

Community
  • 1
  • 1
DBB
  • 1
  • 3