c++overloadingc++17name-collision

Calling non-member operator overloads


Given the following code:

#include <iostream>
#include <functional>
#include <utility>

template<class O, class T, class = void>
constexpr bool ostreamable_with = false;

template<class O, class T> // If there's user-defined overloads
constexpr bool ostreamable_with<
    O, T, std::void_t<decltype(operator<<(std::declval<O>(),
                                          std::declval<T>()))>> = true;

struct jostream : std::reference_wrapper<std::ostream>
{
    using reference_wrapper::reference_wrapper;
    std::ostream& os() { return *this; }

    template<class T>
    jostream& operator<<(T const& v)
    {
        if constexpr(ostreamable_with<jostream&, T const&>)
            // This enables user-defined conversion on `v` too
            operator<<(*this, v); // #1
        else
            os() << v;

        return *this;
    }
};

namespace user {
    struct C
    { int a; };

    inline jostream& operator<<(jostream& os, C const& c)
    { return os << c.a; }
}

int main()
{
    jostream jos(std::cout);
    user::C u{1};
    jos << std::cref(u);
}

In line #1, there's a compiler error because jostream has a function member called operator<<, thus the call in line #1 (the commented line inside jostream::operator<<, not the first line of the code) is trying to make a explicit call to jostream::operator<< with two parameters, which doesn't exist.

Is there any trick to force a call to a non-member function with colliding names? (apart from calling an external function that makes the actual call). A ::operator<< call is clearly not a solution here because the overload could be inside a user namespace, as the example shows.

(using gcc-7.2.0)


Solution

  • using std::operator<<;
    operator<<(*this, v);
    

    std is an associated namespace of *this anyway, so this doesn't introduce anything new into the overload set. Alternatively, define a namespace-scope operator<< taking some dummy type and pull that in with using.