Reputation: 8843
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...
Upvotes: 6
Views: 232
Reputation: 6131
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.
Upvotes: 0
Reputation: 275340
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 optional
s 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.
Upvotes: 8