Enum enum and static const in template class

I want to check if the class is accessible to ostream& by looking at whether overloading is enabled for operator<< . Based on these posts, I tried to write another version using C ++ 11. This is my attempt:

 #include <iostream> #include <type_traits> namespace TEST{ class NotDefined{}; template<typename T> NotDefined& operator << (::std::ostream&, const T&); template <typename T> struct StreamInsertionExists { static std::ostream &s; static T const &t; enum { value = std::is_same<decltype(s << t), NotDefined>() }; }; } struct A{ int val; friend ::std::ostream& operator<<(::std::ostream&, const A&); }; ::std::ostream& operator<<(::std::ostream& os, const A& a) { os << a.val; return os; } struct B{}; int main() { std::cout << TEST::StreamInsertionExists<A>::value << std::endl; std::cout << TEST::StreamInsertionExists<B>::value << std::endl; } 

But this does not compile:

 test_oper.cpp:40:57: error: reference to overloaded function could not be resolved; did you mean to call it? std::cout << TEST::StreamInsertionExists<A>::value << std::endl; /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ostream:1020:1: note: possible target for call endl(basic_ostream<_CharT, _Traits>& __os) test_oper.cpp:30:17: note: candidate function not viable: no known conversion from 'TEST::NotDefined' to '::std::ostream &' (aka 'basic_ostream<char> &') for 1st argument ::std::ostream& operator<<(::std::ostream& os, const A& a) test_oper.cpp:15:15: note: candidate template ignored: couldn't infer template argument 'T' NotDefined& operator << (::std::ostream&, const T&); 

However, if I replace the line enum { value = std::is_same<decltype(s << t), NotDefined>() };
from
static const bool value = std::is_same<decltype(s << t), NotDefined>();
then everything compiles.

Why is there such a difference between enum and bool ?

+5
source share
2 answers

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 { }; 
+4
source

For some reason, I cannot get Barry's solution to work with VS2013 (vc120 pack 3), the replacement fails, so stream_insertion_exists :: value always returns true (any idea why?)

Based on the metaprogram: function definition failure Defines a single function , it works here:

 template <typename T> auto has_stream_insertion_helper( ... ) // ... to disambiguate call -> std::false_type; template <typename T> auto has_stream_insertion_helper( int ) // int to disambiguate call -> decltype( std::declval<std::ostream&>() << std::declval<T>(), std::true_type{} ); template <typename T> using has_stream_insertion = decltype( has_stream_insertion_helper<T>( 0 ) ); class ClassNoStream {}; class ClassHasStream {}; std::ostream& operator<<( std::ostream&, const ClassHasStream& ); static_assert( has_stream_insertion< int >::value, "" ); static_assert( !has_stream_insertion< ClassNoStream >::value, "" ); static_assert( has_stream_insertion< ClassHasStream >::value, "" ); 
+1
source

All Articles