Given the following code:
#include <string>
#include <type_traits>
#include <sstream>
#include <vector>
#include <iostream>
using namespace std;
namespace has_insertion_operator_impl {
typedef char no;
typedef char yes[2];
struct any_t {
template <typename T>
any_t(T const&);
};
no operator<<(ostream const&, any_t const&);
yes& test(ostream&);
no test(no);
template <typename T>
struct has_insertion_operator {
static ostream& s;
static T const& t;
static bool const value = sizeof(test(s << t)) == sizeof(yes);
};
}
template <typename T>
struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T> {};
template <class T>
typename enable_if<has_insertion_operator<T>::value, string>::type stringify(const T& in) {
stringstream stream;
stream << in;
return stream.str();
}
template <class T>
typename enable_if< ! has_insertion_operator<T>::value, string>::type stringify(const T&) {
return "{?}";
}
// ======= OVERLOADS PROVIDED BY THE USER =======
template<typename T, typename T2>
struct myType { T data; T2 op; };
template<typename T, typename T2>
ostream& operator<<(ostream& s, const myType<T, T2>&) { s << "myType"; return s; }
template<typename T>
ostream& operator<<(ostream& s, const vector<T>&) { s << "vector<T>"; return s; }
template<typename T, typename A>
ostream& operator<<(ostream& s, const vector<T, A>&) { s << "vector<T, A>"; return s; }
int main() {
myType<int, float> a; cout << stringify(a) << endl; // prints "myType"
cout << stringify(6) << endl; // prints "6"
vector<int> v(5); cout << stringify(v) << endl; // prints "{?}"
return 0;
}
Why does the template myType<>
get stringified but the templated vector<>
type doesn't?
For the vector<>
type I get the default {?}
stringification but I clearly want one of the overloads at the bottom to be called - just like with myType<>
EDIT:
The actual question here is Why is has_insertion_operator<vector<int>>
false?
I also need this in C++98
And the operator<<
overloads should be supplied after stringify()
- like with myType<>
I think the problem is with the lookup and I will provide my understanding.
When you call the operator<<
in your has...
struct argument dependent lookup kicks in and since myType
lives in the same namespace that the overloaded operator<<
it gets founded and you get a correct string. But when you try to output a vector
it tries to search for the overloaded operator<<
by the same argument-dependent lookup rules and fails to do it since there are no overloaded operators in std
namespace. So it falls back to the unqualified search which starts from the namespace where the call is made, hence it finds your stub operator<<
So in order to fix it you might have placed operator<<
overloads to the std
namespace(which is forbidden by the Standard) or remove your namespace — it will give the same effect.
You don't have to put everything out of the private namespace, though. It is enough to do something like this in the global namespace:
typedef char no;
typedef char yes[2];
template<typename T>
no operator<<(ostream const&, T const&);
Or, if it is possible, it is better to require the library users to put their overloads into the same namespaces where their classes live. It will not work with std
members, though.