当我尝试编写std::cout << x
并且左移位运算符没有为x
定义时,我对编译错误感到有点恼火。无法将x转换为此,无法将x转换为...无效错误消息的几个屏幕。
我想为所有尚未定义此类运算符的类型专门化operator<<(std::ostream&, const T&)
。在内部,我可以将静态断言和编译错误消息放得比现在更清晰。
我的第一次尝试如下。
template<typename T, typename = void>
struct Has : public std::false_type {};
template<typename T>
struct Has<T, decltype(void(
std::declval<std::ostream&>() << std::declval<T>()
))> : public std::true_type {};
template<typename T>
auto operator<<(std::ostream& out, const T&)
-> typename std::enable_if<
!Has<T>::value,
std::ostream&>::type
{
return out << "my operator";
}
无法编译,因为超出了最大模板深度。确实,我的operator<<
调用Has
专门化,以便调用operator<<
,再次检查我的重载,依此类推,等等。
最简单的版本不起作用:std::ostream& << const char*
的模糊重载。好吧,期待。
template<typename T>
std::ostream& operator<<(std::ostream& out, const T&)
{
return out << "my operator";
}
我该如何完成任务?或者,一般来说,如何为所有参数类型定义函数,但是对于那些已经传递给函数的函数?
答案 0 :(得分:1)
一般情况下你不能。但这是c++,所以如果你愿意变得邪恶,你就可以。
namespace named_operator {
template<class D>struct make_operator{constexpr make_operator(){}};
template<class T, char, class O> struct half_apply { T&& lhs; O const& o; };
template<class Lhs, class Op>
half_apply<Lhs, '<', Op> operator<( Lhs&& lhs, make_operator<Op>const & o ) {
return {std::forward<Lhs>(lhs), o};
}
template<class Lhs, class Op, class Rhs>
auto operator<( half_apply<Lhs, '<', Op>&& lhs, Rhs&& rhs )
-> decltype( named_invoke( std::forward<Lhs>(lhs.lhs), lhs.o, std::forward<Rhs>(rhs) ) )
{
return named_invoke( std::forward<Lhs>(lhs.lhs), lhs.o, std::forward<Rhs>(rhs) );
}
}
namespace utility {
namespace details {
template<class...>struct voider{using type=void;};
}
template<class...Ts>using void_t=typename details::voider<Ts...>::type;
namespace details {
template<template<class...>class, class, class...>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
}
namespace streaming {
namespace details {
template<class T>
using ostream_r = decltype( std::cout << std::declval<T&&>() );
}
template<class T>
using can_ostream = utility::can_apply<details::ostream_r, T>;
struct out_tag: named_operator::make_operator<out_tag> {};
static const out_tag out;
template<class T>
std::ostream& named_invoke( std::ostream& os, out_tag, T const& t ) {
static_assert( can_ostream<T const&>{}, "This type cannot be streamed" );
return os<<t;
}
template<class T,
std::enable_if_t< can_ostream<T const&>{}, int> =0 // breaks MSVC
>
std::ostream& named_invoke( std::ostream& os, out_tag, T const& t ) {
return os<<t;
}
}
如果我写得对,
struct no_worky {};
no_worky bob;
using streaming::out;
std::cout <out< bob;
无法编译并生成友好消息,同时
std::cout <out< 7;
致电std::cout << 7
。
我认为这不值得。
答案 1 :(得分:0)
一个答案可能是将ostream包裹在一个薄的包装中以进行管道操作?
这个薄包装器可以具有通用模板操作符&lt;&lt;会员, 然后检查包装的std :: ostream上的真实运算符。 但是,您可能会发现您收到的错误信息更加令人愉快!
请注意,您还需要为std :: endl及其亲属添加一个特定的处理程序,因为他们希望专注于您已经混淆的流宽度。