我刚刚发现如何检查是否为某个类型提供了
operator<<
。
template<class T> T& lvalue_of_type();
template<class T> T rvalue_of_type();
template<class T>
struct is_printable
{
template<class U> static char test(char(*)[sizeof(
lvalue_of_type<std::ostream>() << rvalue_of_type<U>()
)]);
template<class U> static long test(...);
enum { value = 1 == sizeof test<T>(0) };
typedef boost::integral_constant<bool, value> type;
};
这个技巧是众所周知的,还是我刚刚获得了元编程诺贝尔奖? ;)
编辑:我通过两个全局函数模板声明
lvalue_of_type
和rvalue_of_type
使代码更易于理解和更容易适应。
恐怕这是一项众所周知的技术:-)
当然,在
sizeof
运算符中使用函数调用会指示编译器在编译时执行参数推导和函数匹配。此外,通过模板函数,编译器还可以从模板实例化具体函数。但是,该表达式不会导致生成函数调用。 SFINAE Sono Buoni PDF 中有详细描述。
查看其他 C++ SFINAE 示例。
这只是两个众所周知的技巧的组合。 SFINAE 说“替换失败不是错误”——这正是您所做的。使用
sizeof
让编译器将模板参数替换为表达式而不实际执行它也很常见。
抱歉:-)
它曾经是一种惯用技术,但它已经被新的语言版本完全废弃了。
std::declval
(C++11)template<class T> T& lvalue_of_type();
template<class T> T rvalue_of_type();
对于左值,已被
std::declval<T&>()
取代,对于 x 值,std::declval<T>()
已被取代。值得注意的是 std::declval
永远不会返回值,部分原因是这需要非私有析构函数。
requires
表达式和 concept
s (C++20)这种技术也已经过时了。 检查表达式是否...
stream << object; // where stream is std::ostream&, and object is T&
...有效,您可以写一个
concept
:
template <typename T>
concept printable = requires (std::ostream& stream, T& object) {
stream << object;
{ stream << object; } -> std::same_as<std::ostream&>;
};