This question follows my previous question : Generic operator<< ostream C++ for stringifiable class where I would like to implement a generic <<ostream
operator which would work for any class which owns a to_str()
method.
I have succeeded checking whether a class implements a to_str()
method and use std::cout << stringify(a)
thanks to this answer. However, I have difficulties writing template ostream<<
operators to make std::cout << a
works.
The following test code :
#include <iostream>
#include <sstream>
#include <string>
template<class ...> using void_t = void;
template<typename T, typename = void>
struct has_to_string
: std::false_type { };
template<typename T>
struct has_to_string<T,
void_t<decltype(std::declval<T>().to_str())>
>
: std::true_type { };
template<typename T> std::enable_if_t<has_to_string<T>::value, std::string>
stringify(T t) {
return t.to_str();
}
template<typename T> std::enable_if_t<!has_to_string<T>::value, std::string>
stringify(T t) {
return static_cast<std::ostringstream&>(std::ostringstream() << t).str();
}
// The following does not work
/*
template<typename T> std::enable_if_t<has_to_string<T>::value, std::ostream&>
operator<<(std::ostream& os, const T& t) {
os << t.to_str();
return os;
}
template<typename T> std::enable_if_t<!has_to_string<T>::value, std::ostream&>
operator<<(std::ostream& os, const T& t) {
os << t;
return os;
}
*/
struct A {
int a;
std::string to_str() const { return std::to_string(a); }
};
struct B {
std::string b;
std::string to_str() const { return b; }
};
int main() {
A a{3};
B b{"hello"};
std::cout << stringify(a) << stringify(b) << std::endl; // This works but I don't want to use stringify
// std::cout << a << b << std::endl; // I want this but it does not work
}
gives the same error as in the original question. What am I doing wrong ?
You get an ambiguous overload for 'operator<< error when the type is std::string, because the templated version in your code has an equal precedence with the one shipped in the ostream header.
You can check that this is the source of your problem by changing your test program with this:
int main() {
std::cout << std::string("There is your problem") << std::endl;
}
And you shall still see the same error.
To solve the problem, you can add an explicit definition of operator<< that will take precedence over the two conflicting templates.
std::ostream& operator<<(std::ostream& os, const std::string& t) {
using std::operator<<;
os << t;
return os;
}