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.