tags:

views:

109

answers:

2

The following won't compile for me. I'm out of ideas... Any help?

template<>
inline
std::ostream& operator<< <const std::map<std::string, std::string> > (std::ostream& stream, const std::map<std::string, std::string>& some_map)
{
  return stream;
}

g++ gives me the following error:

error: expected initializer before '<' token

Edit: 1 Okay, since everyone is telling me to overload, let me give you an example that wouldn't make sense for overloading. What if I have this:

template <typename T>
inline
std::ostream& operator<<(std::ostream& stream, const T& something)
{
  stream << something.toString();
  return stream;
}

class Foo
{
public:
  Foo(std::string s)
  {
    name = s;
  }

  std::string toString() const 
  {
    return name;
  }

private:
  std::string name;
};

class Bar
{
public:
  Bar(int i)
  {
    val = i;
  }

  std::string toString() const 
  {
    std::ostringstream stream;
    stream << val;
    return stream.str();
  }

private:
  int val;
};

int main(int, char**)
{
  Foo foo("hey");
  Bar bar(2);

  std::cout << foo << std::endl;
  std::cout << bar << std::endl;
  return 0;
}

Now this won't work either.

I just want to avoid having to overload operator<< over and over by using a template like above. This seems like it should be possible. I would like to know if it is, and if so, how?

In such a scenario, overloading for both Foo and Bar to do the same thing would be a waste, that is why I am trying to avoid it.

Edit: 2 Okay, it appears that I am being misunderstood. Here is another attempt to clarify:

template <typename T>
std::ostream& operator<<(ostream& stream, const T& t)
{
  for(typename T::const_iterator i = t.begin(), end = t.end(); i != end; ++i)
  {
    stream << *i;
  }
  return stream;
}

int main(int, char**) 
{
  set<int> foo;
  list<string> bar;
  vector<double> baz;

  cout << foo << " " bar << " " << baz << endl;
};

The above code won't work mind you. Complains about ambiguity. But it seems like the better solution for printing out containers. If I did it the with overloading, I would need to write a version of operator<< for each container/datatype combination, which would yield a ridiculous amount of code duplication.

+5  A: 

This doesn't need to be a template function.

std::ostream & operator<<(std::ostream & stream, const std::map<std::string, std::string> & some_map)
{
    return stream;
}

Edit:

In reference to my comment about writing Java in C++(and sorry if it sounded rude, I didn't intend to be smarmy). Tell me if this doesn't work better for you. Instead of writing a "toString" method in the first place, just overload the operator<< to begin with. The function is nearly identical. Then, you can write a non-member template toString function that will automatically work with all of your classes, like this:

#include <sstream>
#include <string>

template<typename T>
std::string toString(const T & val)
{
    std::ostringstream ostr;
    ostr << val;
    return ostr.str();
}

Edit 2

Here's my alternative if you still insist on doing it your way. Make all of your classes with the toString method inherit from an abstract class with a virtual toString method, then write one operator<< to handle all of them.

class Stringifiable
{
public:
    virtual std::string toString() const = 0;
};

std::ostream & operator<<(std::ostream & ostr, const Stringifiable& something)
{
    return ostr << something.toString();
}

Now the compiler will choose your overload over templates.

PigBen
+1: This solves the problem, and does in keeping with the C++ stream paradigm.
Oli Charlesworth
The problem with the questioner's second example, seems to be that the compiler is getting confused with the templated operator<< and the use of the ostream's member operator<< in the templates implemetation. I think that confusion will only get worse. Thus, this solution seems the most reasonable.
Dusty Campbell
+1  A: 

You can use SFINAE to remove the template overload from consideration. Note that this has to be a part of the signature of the function. Thus you can use boost::enable_if:

template < typename T >
typename boost::enable_if< meta_function_to_check_for_concept<T>, std::ostream&>::type
operator << (std::ostream & out, T const& t)
{
  ...
}

If you don't do this then your template will attempt to match almost anything and will explode for every use of << that is not in line with the concept you're trying to match.

Your example is a bit contrived and might not lend itself to this answer, but there are situations in which it's warranted. This is how to do it.

Noah Roberts
I never heard of SFINAE until you mentioned it. I am reading up on it right now. Because of you I learned something new. Thank you. From your answer I'm going to try and derive a solution that doesn't use boost.
anio