我在https://stackoverflow.com/a/51951315/1908650中询问了以下内容:
我想让
template<class T> ostream& operator<<(ostream& os, const optional<unique_ptr<T>>&
超载。
在评论中,@ Yakk-Adam Nevraumont指出:
该问题的答案是“你不能”。对于通用类型T,没有很好的合法方法;我可以解释原因,但这需要一个新的问题/答案
我正在创建一个新的问号以接受该报价...
答案 0 :(得分:8)
在与类型关联的名称空间中重载运算符的适当位置。
对于std::optional<std::unique_ptr<T>>
,始终存在一个关联的命名空间std
(来自ostream
和optional
和unique_ptr
),以及与之关联的任何命名空间T
。由于您想对所有类型都重载,因此与T
关联的名称空间对您没有用。
在std
中引入新功能或重载是不合法的;在某些有限的情况下,您可以引入专业化知识,但在此没有一个适用。向<<
添加新的std
重载会使您的程序格式错误,无需进行诊断。
您可以(A)使用您自己的命名空间中经过修饰的unique_ptr
或optional
,或者(B)使用经过修饰的ostream
,或者(C)编写格式化程序包装器:< / p>
namespace fmt {
template<class T>
struct wrapper_t {
T&& t;
};
template<class T>
struct is_wrapped:std::false_type{};
template<class T>
struct is_wrapped<wrapper_t<T>>:std::true_type{};
template<class OS, class T,
std::enable_if_t<!is_wrapped<OS&>{}, bool> =true
>
auto operator<<( OS& os, wrapper_t<T>&& w )
-> decltype( os << std::forward<T>(w.t) )
{ return os << std::forward<T>(w.t); }
template<class OS, class T>
auto operator<<( wrapper_t<OS&> os, T&& t )
-> decltype( os.t << std::forward<T>(t) )
{ return os.t << std::forward<T>(t); }
template<class T>
wrapper_t<T> wrap(T&& t){ return {std::forward<T>(t)}; }
}
然后,std::cout << fmt::wrap( foo )
可以在<<
中找到fmt
的重载,如果找不到重载,则对包含的数据调用<<
。
这也支持fmt::wrap(std::cout)
而不是包装参数。可能有错别字。
答案 1 :(得分:0)
除了已经说过的话,还有另一个涉及ODR问题的问题。如果重载了您无法控制的类型的函数,则可以想象其他人也以不同的方式重载了该函数。然后,您使用重载来编译代码,他们使用重载来编译代码,并且当您在同一家公司工作时,最终会在不同的翻译单元中使用相同签名功能的多个版本。同样,行为不确定,无需诊断。
消毒剂可能会发现这一点,也许听起来是人为的,但这种事情时有发生。