Why is this error occurring, and what can I do to pass the return value directly to the constructor?
The constructor of ValuesConstraint<>
accepts an lvalue (lvalue references to non-const
can only bind to lvalues), while function call expressions that do not return a reference type are rvalues.
lvalue references cannot bind to rvalues, so you cannot pass a temporary to the constructor of ValuesConstraint<>
(temporaries are rvalues).
If you find the terms lvalue and rvalue confusing, you can (intuitively) think of lvalues as:
- named variables (actually, identifiers); and/or
- objects (actually, expressions) you can take the address of
They usually denote objects that can be repeatably referenced in a program, with a stable identity, from which it is not safe to move implicitly (because they could be referenced later on in a program).
On the contrary, you can see rvalues as:
- unnamed objects like temporaries; and/or
- literals (like
42
, false
, and 3.14
); and/or
- expressions denoting objects with an "unstable" identity (like the result of
std::move()
), which are about to be moved from
They usually denote objects that cannot be repeatably referenced in a program (or objects you promised not to reference anymore before reassigning them by explicitly calling std::move()
) and are, therefore, safe to move from.
Take the above with a grain of salt though - it is just a guideline you can use to build some intuition on what value categories are, and how to tell what is the value category of a certain expression. In formal terms, things are a bit more complicated and the above generalizations are not always correct.
So getting back to your example, in this situation:
std::vector<std::string> v = getNames();
// ^
// Named variable! Can take its address! lvalue!
ValuesConstraint<std::string> A( v );
// ^
// OK! The constructor accepts an lvalue
// reference to non-const, and I am giving
// it an lvalue
You are passing an lvalue to the constructor and everything works fine. In this situation, on the other hand:
ValuesConstraint<std::string> A( getNames() );
// ^^^^^^^^^^
// getNames() returns a vector by value, this
// means a temporary will be constructed as the
// return value of the function, and temporaries
// are unnamed... so, that's an rvalue!
// And the constructor accepts an lvalue reference
// to non-const! So this is an ERROR!
You are trying to pass an rvalue to a function that accepts an lvalue reference to non-const
(in other words, it wants a modifiable lvalue). That's illegal, and the compiler complains about it.
This occurs in GCC 4.4.7 but not in VS2010
That is because VC10 implements a non-conforming compiler extension that allows binding rvalues to lvalue references to non-const
. Watch out though, this is not standard behavior, and if you want to write portable code, you shouldn't rely on it (I'd suggest you not to rely on it anyway, actually).
As suggested by Jesse Good in the comments, raise the warning level to 4 in order to get a warning from the compiler and detect situations where you are making use of this non-standard extension.