为什么对于涉及第三方代码的模板类无法覆盖operator <<?

时间:2018-08-21 18:11:27

标签: c++ overloading

我在https://stackoverflow.com/a/51951315/1908650中询问了以下内容:

  

我想让template<class T> ostream& operator<<(ostream& os, const optional<unique_ptr<T>>&超载。

在评论中,@ Yakk-Adam Nevraumont指出:

  

该问题的答案是“你不能”。对于通用类型T,没有很好的合法方法;我可以解释原因,但这需要一个新的问题/答案

我正在创建一个新的问号以接受该报价...

2 个答案:

答案 0 :(得分:8)

在与类型关联的名称空间中重载运算符的适当位置。

对于std::optional<std::unique_ptr<T>>,始终存在一个关联的命名空间std(来自ostreamoptionalunique_ptr),以及与之关联的任何命名空间T。由于您想对所有类型都重载,因此与T关联的名称空间对您没有用。

std中引入新功能或重载是不合法的;在某些有限的情况下,您可以引入专业化知识,但在此没有一个适用。向<<添加新的std重载会使您的程序格式错误,无需进行诊断。

您可以(A)使用您自己的命名空间中经过修饰的unique_ptroptional,或者(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问题的问题。如果重载了您无法控制的类型的函数,则可以想象其他人也以不同的方式重载了该函数。然后,您使用重载来编译代码,他们使用重载来编译代码,并且当您在同一家公司工作时,最终会在不同的翻译单元中使用相同签名功能的多个版本。同样,行为不确定,无需诊断。

消毒剂可能会发现这一点,也许听起来是人为的,但这种事情时有发生。