6

The following code does not compile in clang 3.5+, but compiles in all gcc versions and clang < 3.5.

struct Logger {
    template <class T>
    Logger& operator<<(const T& /*val*/)
    {
        // m_buf << val;
        return *this;
    }
};

struct Value {
    template <class Stream>
    friend Stream& operator<<(Stream& os, const Value& /*val*/)
    {
        // os << m_val;
        return os;
    }
};

int main()
{
    Logger logger;
    Value value;
    logger << value;
    return 0;
}

The clang error is

<source>:23:12: error: use of overloaded operator '<<' is ambiguous (with operand types 'Logger' and 'Value')

    logger << value;

    ~~~~~~ ^  ~~~~~

<source>:3:13: note: candidate function [with T = Value]

    Logger& operator<<(const T& /*val*/)

            ^

<source>:12:20: note: candidate function [with Stream = Logger]

    friend Stream& operator<<(Stream& os, const Value& /*val*/)

                   ^

Who is correct according to the standard, clang or gcc? If clang, how do I properly write a logger with "<<" that redirects any value supporting "<<" to another stream?

cigien
  • 57,834
  • 11
  • 73
  • 112
amosk
  • 385
  • 1
  • 11
  • 3
    related: https://stackoverflow.com/questions/52793199/disambiguation-of-friend-and-member-binary-operator probably duplicate – mch Nov 04 '20 at 14:54
  • 1
    Use SFINAE (or concepts!) to restrict the standalone `operator<<` to `std::basic_stream<>` instantiations. – n. m. could be an AI Nov 04 '20 at 14:58
  • @Harry, m_val is a different type (some internal representation of Value), no recursion here – amosk Nov 04 '20 at 15:13
  • @n.'pronouns'm. does not work for non-std::basic_stream stream-like objects (like other loggers) – amosk Nov 04 '20 at 15:17
  • @Harry yes, the commented line should be `os << val.m_val;` or something like that, which is irrelevant to the topic – amosk Nov 04 '20 at 15:38
  • 1
    Well of course it doesn't. Either `Value` implements printing to stream-like objects, or stream-like objects do that. Both at the same time cannot, that's ambiguous. You need to exclude one or the other from the overload set. – n. m. could be an AI Nov 04 '20 at 16:20
  • @n.'pronouns'm. but this compiles in gcc. First it calls Value's friend function and then Logger's operator< – Harry Nov 04 '20 at 16:23
  • It's a gcc bug. Can you explain why it prefers Value's friend function? There is no reason to do that in the standard. Neither the friend nor the member are better than the other. – n. m. could be an AI Nov 04 '20 at 16:40

0 Answers0