value is an enumeration of the anonymous name in StreamInsertionExists<T> . When you try to do:
std::cout << StreamInsertionExists<T>::value;
The compiler searches for overloads on operator<<(std::ostream&, StreamInsertionExists<T>::E) . In a typical case, he would do an integral advance on enum and pass it as an int . However, you additionally defined this statement:
template<typename T> NotDefined& operator << (std::ostream&, const T&);
This is a better match for enum than the int version (exact match against whole ads), so this is preferable. Yes, this is a function template, but non-templates are preferable only if the conversion sequences match - in which case they do not.
So this line:
std::cout << TEST::StreamInsertionExists<A>::value << std::endl;
which the:
operator<<(operator<<(std::cout, TEST::StreamInsertionExists<A>::value), std::endl);
And the operator<< inner call operator<< will use your NotDefined& template. Thus, the next step would be to find the appropriate function call for:
operator<<(NotDefined&, std::endl);
and there is no such overload for operator<< , therefore, errors.
If you change the value to bool , there is no such problem, because there is an operator<< that accepts bool sure: # 6 . However, even with bool your trait is still incorrect, as it always returns false . Your version of NotDefined actually returns the link, so you have to check it out. NotDefined& means no , so you have to flip the sign:
static const bool value = !std::is_same<decltype(s << t), NotDefined&>();
However, this is especially error prone. Now cout << B{}; instead of compiling, it will give you a linker error instead, and cout << B{} << endl; will give you the same confusing overload error with endl instead of simple stats that you cannot pass stream B
You should prefer simply:
template <typename...> using void_t = void; template <typename T, typename = void> struct stream_insertion_exists : std::false_type { }; template <typename T> struct stream_insertion_exists<T, void_t< decltype(std::declval<std::ostream&>() << std::declval<T>()) > > : std::true_type { };