0

So I overloaded the ostream << operator so it can take most STL containers and print them. However, it doesn't work for passing strings (the error is "ambiguous overload" for the line "cout << s;"). How do I make it so it works on strings as if it weren't overloaded?

#include <bits/stdc++.h>
using namespace std;

template<typename T>
ostream& _containerprint(ostream &out, T const &val) {
  return (out << val << " ");
}

template<typename T1, typename T2>
ostream& _containerprint(ostream &out, pair<T1, T2> const &val) {
  return (out << "{" << val.first << " " << val.second << "} ");
}

template<template<typename, typename...> class TT, typename... Args>
ostream& operator<<(ostream &out, TT<Args...> const &cont) {
  for(auto&& elem : cont) {
    _containerprint(out, elem);
  }
  return out;
}

int main() {
  string s = "help me";
  cout << s;
}

Edit: please stop freaking out, the #include <bits/stdc++.h> is because it's for a programming contest setting; it really doesn't matter!

  • `#include ` -- Use the proper `#include` files, not this one. – PaulMcKenzie Sep 19 '18 at 16:38
  • 1
    You probably can use `std::enable_if` – Slava Sep 19 '18 at 16:40
  • Expanding on Paul's comment, `#include ` should not be used ([why](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h)) and `using namespace std;` should be avoided ([why](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice)). Together they reinforce some of the other's worst behaviours, resulting in some very hard to understand errors. Do not do this. – user4581301 Sep 19 '18 at 16:45
  • It doesn't matter if I use bits/stdc++.h because it's in a competitive programming setting. – Vladimir Sharkovski Sep 19 '18 at 18:40
  • It matters when you have a naming collision between one of your identifiers and something you didn't even know existed in the standard library eating away at all of your time. Plus bad habits tend to leak into other aspects of one's life. – user4581301 Sep 19 '18 at 19:33

2 Answers2

4

std::string is a template class (alias of std::basic_string<char>)

so std::cout << s match your overload and the one for std::basic_string<T>.

And none overload is more specialized than the other.

Possible workaround is to add additional overload for std::string:

std::ostream& operator << (std::ostream& out, const std::string& s)
{
    return operator << <char>(out, s);
    // Select template <class CharT, class Traits, class Allocator>
    // std::basic_ostream<CharT, Traits>& 
    // operator<<(std::basic_ostream<CharT, Traits>& os,
    //            const std::basic_string<CharT, Traits, Allocator>& str);
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • That#s exactly why the OP shouldn't `#include using namespace std;` in 1st place. – πάντα ῥεῖ Sep 19 '18 at 16:30
  • @πάνταῥεῖ: Happens even with `#include ` (needed to allow `out << "{"`). (that's why I removed duplicate Btw). (But I agree that OP should not `#include `). – Jarod42 Sep 19 '18 at 16:32
0

Make your ostream parameter more general so that your overload is more general than the standard one for std::string.

template<typename CharT, typename Traits, typename T>
ostream& _containerprint(std::basic_ostream<CharT, Traits> &out, T const &val) {
  return (out << val << " ");
}

template<typename CharT, typename Traits, typename T1, typename T2>
ostream& _containerprint(std::basic_ostream<CharT, Traits> &out, pair<T1, T2> const &val) {
  return (out << "{" << val.first << " " << val.second << "} ");
}

template<typename CharT, typename Traits, template<typename, typename...> class TT, typename... Args>
ostream& operator<<(std::basic_ostream<CharT, Traits> &out, TT<Args...> const &cont) {
  for(auto&& elem : cont) {
    _containerprint(out, elem);
  }
  return out;
}
xskxzr
  • 12,442
  • 12
  • 37
  • 77