Search code examples
templatesc++11operator-overloadingpretty-printiterable

ambiguous operator<< for templated overload


Following code is my first C++11 attempt at pretty printing iterable containers. It uses the function template default parameter feature.

#include <ostream>
#include <string>
#include <utility>

template <typename T>
void print(std::ostream &o, T const &t) { o<< t; }

void print(std::ostream &o, std::string const &s){ o<< '"'<< s<< '"'; }

template <typename K, typename V>
void print(std::ostream &o, std::pair<K, V> const &p)
{
  o<< '{'; print(o, p.first);
  o<< ": "; print(o, p.second);
  o<< '}';
}

template <typename C, typename I= typename C::const_iterator>
std::ostream &operator<< (std::ostream &o, C const &c)
{
  o<< '[';
  if(c.empty()) return o<< ']';
  I b= c.begin(), e= c.end(); -- e;
  for(; b!= e; ++ b)
  {
    print(o, *b);
    o<< ", ";
  }
  print(o, *b);
  return o<< ']';
}

It works fine on containers, container of containers etc. With one exception:

std::cout<< std::string("wtf");

Compilation with g++4.7/8 breaks saying ambiguous operator<<.

Is there any fix for this code to avoid the ambiguity?


Solution

  • You can use std::enable_if to disable your overload in the case of a string:

    template <typename C, typename I= typename C::const_iterator>
    typename std::enable_if<!std::is_same<C,std::string>::value,std::ostream>::type &
      operator<< (std::ostream &o, C const &c)
    {
      o<< '[';
      if(c.empty()) return o<< ']';
      I b= c.begin(), e= c.end(); -- e;
      for(; b!= e; ++ b)
      {
        print(o, *b);
        o<< ", ";
      }
      print(o, *b);
      return o<< ']';
    }
    

    or to do it more generically:

    template <typename T>
    struct is_string : std::false_type {};
    
    template <typename Char,typename Allocator>
    struct is_string<std::basic_string<Char,Allocator> > : std::true_type {};
    
    template <typename C, typename I= typename C::const_iterator>
    typename std::enable_if<!is_string<C>::value,std::ostream>::type &
      operator<< (std::ostream &o, C const &c)
    {
      o<< '[';
      if(c.empty()) return o<< ']';
      I b= c.begin(), e= c.end(); -- e;
      for(; b!= e; ++ b)
      {
        print(o, *b);
        o<< ", ";
      }
      print(o, *b);
      return o<< ']';
    }