0

I have a log builder type like so:

Log Log::log(const int logLevel) 
{ 
    return Log(logLevel); 
}

Log& operator <<(Log& log, const char * s)
{
    if (log.hasLogLevel())
        log.out << s;
    return log;
}

I'm using the above code like this:

Log::log(1) << "Hello logger";

But I'm getting these warnings and it wasn't until recently I realized that it's because the way the operator is overloaded (or at least this is what I'm thinking)

warning C4239: nonstandard extension used : 'argument' : conversion from 'snow::Log' to 'snow::Log &'

I thought this would be fine because it's the same rvalue? that's being passed/chained through these operator overloads. I don't think this code compiles outside of MSVC++ and I would like to know what I should be doing differently here.

If the solution is to simply use rvalue references then I'm cool with that but I'd like to understand a little better what's going on here.

John Leidegren
  • 59,920
  • 20
  • 131
  • 152

1 Answers1

4

The problem is that the Log is an rvalue, and that is not allowed to bind to the non-const reference parameter. Microsoft doesn't enforce this, bacuase they have some legacy code that would break.

If you only want to output strings, one workaround is to make the operator<< a member of the Log class. You are allowed to call members of an rvalue.

If you want to use other non-member operators, you can provide an rvalue to lvalue converter, like the standard streams do in C++11.

Something like

template<class T>
Log& operator<<(Log&& log, const T& value)
{ return log << value; }

using the fact that inside the operator log is an lvalue and can bind to a non-const reference of the other operators.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • OK, but if I change the type of the operator overload to `Log& operator <<(Log&& log, const char * s)` that's fine to, right? And It might be more in line what I want to do here. – John Leidegren Oct 26 '12 at 11:45
  • 1
    @JohnLeidegren what's wrong with making the operator a member? – Luchian Grigore Oct 26 '12 at 11:46
  • Nothing, I'm fine with that... just asking... trying to understand better. – John Leidegren Oct 26 '12 at 11:47
  • I found http://stackoverflow.com/a/4622467/58961 to clarify some parts about the operator overloading thing. Why making it a member makes it behave differently. – John Leidegren Oct 26 '12 at 11:54
  • @John - In this case the operator is assymetric anyway, so having it as a member doesn't make *that* big a difference. It does make a difference if you need both `a + b` and `b + a` when `a` and `b` are different types (or one of them is a built in type). – Bo Persson Oct 26 '12 at 12:06
  • Exactly, I went with the member operator route, the only sad part is that I ended up with a lot of member operators (one for each new user-defined type). That set is very limited though and the template trick can fix some of that. This should be fine, thanks for the help in the matter. – John Leidegren Oct 26 '12 at 12:10