检测 class 是否与 std::ostream<< 兼容的特征

Traits to detect if a class is compatible with std::ostream<<

我试图实现一个 type_traits 能够检测 class 是否可以在指令的上下文中使用,例如:std::cout << my_class_instance;.

我的实现试图从 SFINAE 中获益,以检测函数 std::ostream& operator<<(std::ostream&, const MyClass&) 是否可用于 class。不幸的是,它在应该与 C++ 11 兼容的 g++-4.9 下失败了。其他编译器没有抱怨并且似乎生成了正确的代码:g++-5+、clang++-3.3+ 以及 Visual Studio.

这是我到目前为止尝试过的实现:

#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");

现场示例可在以下网址获得:https://godbolt.org/g/6xFSef。 如果您需要更多详细信息,请不要犹豫。

这是 gcc 解释方式的问题:

template <class... Ts> using void_t = void;

在处理具体影响该问题的核心语言问题之前 (1558)。基本上,gcc 发现这个别名不受模板参数的影响,只是掉落在 void 中。如果总是成功,这完全违背了 void_t 的目的,这就是本例中发生的情况。

你可以通过将 void_t 包装在另一个模板中来欺骗它(如 original paper 中所建议的):

template <class... Ts> struct make_void { using type = void; };            
template <class... Ts> using void_t = typename make_void<Ts...>::type;

在这里,gcc 不能只删除替换,因为假设当然有一些其他 make_void 专业化,而不仅仅是 void