I tried to implement a type_traits able to detect if a class can be used in the context of an instruction such as: std::cout << my_class_instance;
.
My implementation tried to benefit from SFINAE to detect whether or not the function std::ostream& operator<<(std::ostream&, const MyClass&)
is available for the class. Unfortunately it fails under g++-4.9 which should be compatible with C++ 11. Other compilers does not complain and seem to generate the right code: g++-5+, clang++-3.3+ and also Visual Studio.
Here is the implementation I tried so far:
#include <iostream>
#include <type_traits>
#include <vector>
template <class... Ts> using void_t = void;
template <class T, class = void> struct can_be_printed : std::false_type {};
template <class T> struct can_be_printed<T, void_t<decltype(std::cout << std::declval<T>())>> : std::true_type {};
static_assert(!can_be_printed<std::vector<int>>::value, "vector<int> cannot be printed");
static_assert(can_be_printed<int>::value, "int can be printed");
Live example is available at: https://godbolt.org/g/6xFSef . Please do not hesitate if you need more details.
This is an issue with how gcc interprets:
template <class... Ts> using void_t = void;
before it handled the core language issue that specifically affected that (1558). Basically, gcc sees that this alias isn't affected by the template parameters and just drops in void
. That completely defeats the purpose of void_t
if it always succeeds, which is what is happening in this case.
You can fool it by just wrapping void_t
in another template (as suggested in the original paper):
template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;
Here, gcc can't just drop the substituation, since there is of course hypothetically some other make_void
specialization that isn't just void
.