Why is it not possible to override operator<< for template classes involving third-party code?

守給你的承諾、 提交于 2020-01-03 09:07:23

问题


I asked about the following in https://stackoverflow.com/a/51951315/1908650 :

I want to overload template<class T> ostream& operator<<(ostream& os, const optional<unique_ptr<T>>&).

In the comments, @Yakk - Adam Nevraumont notes:

The answer to that question is "you cannot". There is no good legal way to do that for a generic type T; I could explain why, but it would take a new question/answer to do so

I'm creating a new Q. to take up the kind offer...


回答1:


The proper place to overload operators in in the namespace associated with the type.

For std::optional<std::unique_ptr<T>> there is one associated namespace std that is always there (from ostream and optional and unique_ptr), plus whatever namespace is associated with T. As you want to overload for all types, the namespace(s) associated with T are not useful to you.

It is not legal to introduce a new function or overload into std; in certain limited circumstances you can introduce specializations, but none of them apply here. Adding a new overload of << to std makes your program ill formed, no diagnostic required.

You could (A) use decorated unique_ptr or optionals from your own namespace, or (B) use a decorated ostream, or (C) write a formatter wrapper:

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)}; }
}

then std::cout << fmt::wrap( foo ) can find overloads of << within fmt, and if none are found invokes << on the contained data.

This also supports fmt::wrap(std::cout) instead of wrapping the arguments. There are probably typos.




回答2:


In addition to what has been said, there's another issue involving ODR problems. If you overload a function for types you don't control, it is conceivable that someone else also overloads it differently. Then you compile your code with your overload, they compile their code with their overload, and when you work for the same company you end up with multiple versions of the same-signature function in different translation units. Again, undefined behavior, no diagnostic required.

Sanitizers may find this, and perhaps it sounds contrived, but such things happen from time to time.



来源:https://stackoverflow.com/questions/51954454/why-is-it-not-possible-to-override-operator-for-template-classes-involving-thi

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!