I am making a c++ program and I am stuck on the stage of hosting it on linux. (in fact it is an addon for node.js, but it does not matter now). I get an error when compiling my function on linux (on windows everything is OK).
error: cannot bind non-const lvalue reference of type ‘std::__cxx11::string&’ {aka ‘std::__cxx11::basic_string<char>&’} to an rvalue of type ‘std::__cxx11::string’ {aka ‘std::__cxx11::basic_string<char>’}
return(merge(messUp3(a.substr(0, (int)(a.size()) / 2)), messUp3(a.substr((int)(a.size()) / 2))));
the problem is exactly here: a.substr(0, (int)(a.size()) / 2). I have managed to fix the error by deleting & from arguments of functions. string merge(string& a, string& b) -> string merge(string a, string b). I think I have some understanding, why it should not work with &, but then I get a complete misunderstanding of why does it work on windows? How do linux and windows gcc compilers differ? Or the problem is somewhere else?
A reference like std::string & references a value that already exists as an identifiable object in memory (for example by its memory address), which is called an lvalue in C++ terminology.
The return value of a function is a temporary value that has not identity. The compiler is not required to allocate an address for it, which is called an rvalue in C++ terminology.
Thus, you cannot assign an rvalue to an lvalue reference, as the error message says.
(In fact these so-called value categories are a bit more complicated in C++.)
If you replace std::string & by std::string you create a new variable and copy the rvalue into it instead of referencing it, which is ok, because in this case you only read the rvalue instead of referencing it. (To be precise the rvalue will be moved but explaining that is beyond the scope of this answer.)
Your Windows compiler is obviously less strict with the C++ standard by creating a workaround internally instead of pointing out the error. Maybe that compiler has an option to make it more standard-conforming. I'd recommend to turn on such option, particularly if you develop for different platforms, but also to create more clean code.
Related
I've been working on a project recently, and I decided to install ReSharper C++ to Visual Studio. When it analysed my code it spat out a bunch of new warnings (apparently I have bad coding habits..). One of them that took me a while to figure out was Binding r-value to l-value reference is non-standard Microsoft C++ extension. I've recreated the warning with the following code:
Type foo(Type t1, Type t2) {
return Type(t1.value & t2.value);
}
The expression t1.value & t2.value triggers the warning. I understand the second part of the warning, which means that my code only compiles due to a Microsoft extension, and other compilers would refuse to compile it. I'm using an overloaded operator, which returns an object (called Datum) which Type takes as a constructor parameter, as a reference (Type::Type(Datum& dat)).
With some playing around, I managed to make the warning go away by refactoring the code:
Type bar(Type t1, Type t2) {
Datum datum = t1.value & t2.value;
return Type(datum);
}
To my understanding, this is functionally equivalent to the code that generated the warning. What I'd really like to know is whether there's something here that I should be aware of, because I'm pretty confused about why one function complains and one doesn't.
I think I've got it figured out. I already had the question typed out, so I'm going to post it with what I found, for the reference of others. I don't really have enough knowledge to go into detail, so please feel free to expand on or correct my answer if it isn't satisfactory :)
That's one way to remove the warning: the variable is an lvalue, so can bind directly to the dodgy constructor's reference parameter, while the expression result is an rvalue which can't.
A better solution is to fix the constructor to take its argument by value or constant reference:
Type(Datum dat) // value
Type(Datum const & dat) // constant reference
Now you can give the argument as either an lvalue or an rvalue.
From what I can see, the reason I got such behaviour is because the Type constructor is taking a reference to the Datum object, rather than passing it by value. This causes a warning in Type foo(Type, Type) because the compiler doesn't like taking the reference of expressions, which would be due to the semantics of expression evaluation.
Again, please feel free to elaborate on or correct my findings, as this is simply the result of my experimentation and inference.
While compiling (with Linux Server release 6.1)
strftime(AppTime, sizeof(AppTime),"%Y/%m/%d %T", localtime(&((long)u32_Time)));
getting error "error: lvalue required as unary '&' operand"
but the same code compiled successfully with Red Hat Enterprise Linux AS release 3.
Why so? How to correct this?
The address-operator & requires a variable to take the address from. The result of your cast (long)u32_Time is a temporary that does not necessarily reside in memory and therefore has no address that could be taken. So if that piece of code ever compiled somewhere it was a nonstandard compiler extension.
The standard, §5.3.1,3 demands:
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue [...]
How to fix this:
std::localtime expects a pointer to a std::time_t so you best provide that. You did not provide any explanation or further code, so I can only guess that u32_Time is some 4 byte unsigned arithmetic type that is supposed to represent a time in some manner. How that is properly converted into a std::time_t depends on how your compiler implements the latter and how you got the value of the further. Simply applying a C-cast is not portable, and casting to long is even less portable.
If, and only if the std::time_t on your current platform is also a unsigned 32 bit type using the same representation as your u32_Time, it might suffice to use
localtime(reinterpret_cast<std::time_t*>(&u32_Time));
More portable would be storing the value in the correct data type first:
std::time_t time = u32_Time;
localtime(&time);
That way you will get the necessary warnings and/or errors if time_t and the type of u32_Time are not compatible.
I would strongly advice against using C-casts, because once you have to port this piece of code to another platform you will have no means to find that nasty cast easily.
It's probably best to start with what the error means. An "lvalue" is something that appears on the left side of an equals sign. What it means is that your argument to the "address of" operator (&) must be something that you could assign to. In your case, this isn't syntactically correct:
(long)u32_Time = 0;
The reason for this restriction is that the & operator returns the address of something that is stored somewhere. (long)u32_Time isn't stored somewhere, but u32_Time is.
This might have worked ok on more tolerant compilers because it would allocate some space for the long representation of u32_Time and then give you a pointer to that, but I wouldn't count on that working (as can be seen in Linux Server release 6.1).
To fix this, you can just create a new variable and take it's address instead:
long long_Time = (long)u32_Time;
strftime(AppTime, sizeof(AppTime),"%Y/%m/%d %T", localtime(&long_Time));
However, that's still not perfect. localtime expects a time_t pointer, not a long pointer, and while they might be the same on your platform, you'd be better off with:
time_t time_t_Time = (time_t)u32_Time;
strftime(AppTime, sizeof(AppTime),"%Y/%m/%d %T", localtime(&time_t_Time));
I am porting a large project from C++ Builder 2010 to XE4, and have just come across this code. It compiled, ran, and apparently worked in CB2010, and according to the revision log was added when we were still using C++ Builder 2007. Neither of these compilers supported rvalue references.
VOperandValue& VOperandValue::operator=(VOperandValue&& oOther) {
// Assignment operator takes full control of all pointers etc, and sets oOther
// to not own anything
if (static_cast<void*>(this) != static_cast<void*>(&oOther)) {
CopyAndTakeOwnershipFrom(oOther);
}
return *this;
}
void VOperandValue::CopyAndTakeOwnershipFrom(VOperandValue&& oOther) {
// Lots of assignments to self's members, and clearing fields of oOther
}
Two things to note:
operator= changing the copied-from object is deliberate. This class wraps resources that can only be owned by one object at a time. It's beyond the scope of this question, but the behaviour, while odd, is as designed. There is also a copy constructor and a few others methods that have been changed similarly. The methods are intended for use where the copied-from objects are either about to become invalid (eg destroyed) or will be re-used to hold something new. I have no idea why static_cast<void*> is necessary for comparing the pointers to objects of the same type; it seems a very odd thing to do, to me.
The code was changed from using standard references (ie VOperandValue& oOther) to rvalue-references. Despite support for rvalue references only being added in C++Builder XE, the version after C++Builder 2010, the compiler happily accepted and compiled it, and the code appears to have worked when run.
Now this code is loaded into XE4, it fails to compile on the line CopyAndTakeOwnershipFrom(oOther) with the following errors:
[bcc32 Error] OperandValue.cpp(178): E2559 Can't initialize rvalue
reference of type 'VOperandValue' with lvalue of type 'VOperandValue'
[bcc32 Error] OperandValue.cpp(178): E2342 Type mismatch in parameter
'oOther' (wanted 'VOperandValue &&', got 'VOperandValue')
(I don't understand these errors, since line 178 is the line CopyAndTakeOwnershipFrom(oOther);, and oOther appears to definitely have been defined in the method parameter list as an rvalue-reference. Why then is it having trouble with an lvalue non-r-ref when passing the same variable?)
I have two questions, a primary practical question and a secondary curiosity question:
Primary: The coder who changed this code from using standard references to rvalue-references presumably did so thinking that move semantics were best in this situation. I can understand that, though assignment involves copying pointer values only, which isn't much work. How would I, if they're suitable, correctly use rvalue-references for this code?
Secondary: (Curiosity only.) What did the 2007 and 2010 compilers make of this code? Was it read as a reference to a reference? Did the two & operators coalesce and become a single reference? Since it was, presumably, invalid syntax, yet it compiled and worked fine, what on earth was it doing?
As a solution to your problem you can use std::forward.
Just change your code to:
#include <utility>
CopyAndTakeOwnershipFrom(std::forward<VOperandValue>(oOther));
The problem arises because you want to forward move semantics through 2 functions but in the first function the moved object gets a name. With a name its not an rvalue anymore without std::forward but an lvalue that cannot be used for a T&& parameter in CopyAndTakeOwnershipFrom.
That it worked before in C++ Builder 2010 seems to be a bug there which got fixed in XE4.
MS VS x86 compiler has no problem with the following definitions, but GCC (ARM) complains. Is GCC dumb or is MSVS_x86 too clever?
bool checkPointInside(const CIwVec2& globalPoint) {
return checkPointIn(globalPoint, CIwVec2());
}; /// error: no matching function for call to 'Fair::Sprite::checkPointIn(const CIwVec2&, CIwVec2)'
bool checkPointIn(const CIwVec2& globalPoint, CIwVec2& localPoint) {
return false;
};
According to the C++ standard, you cannot bind an rvalue to a non-const reference. The Microsoft compiler has an evil extension that allows this, however. So g++ is correct not to accept your program.
g++ is right to complain and Microsoft has this wrong. The problem with this code is that the checkPointIn function takes it's second parameter by reference, meaning that it must take an lvalue (a variable, or a dereferenced pointer, for example). However, the code in checkPointInside is passing in a temporary object, which is an rvalue. For historical reasons the Microsoft compiler allows this, though it's explicitly forbidden by the spec. Usually, if you crank the warning level all the way up in the Microsoft compiler, it will indeed flag this code as erroneous.
To fix this, either have checkPointIn take its last argument by value or by const reference. The latter is probably the better choice, since const references can bind to rvalues if necessary and avoid making costly copies in other cases.
You can't create references to temporaries (only constant references or r-value references in C++0x).
This is happening when you call checkPoint with CIwVec2() as second parameter.
§5.3.1 Unary operators, Section 3
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id.
What exactly does "shall be" mean in this context? Does it mean it's an error to take the address of a temporary? I was just wondering, because g++ only gives me a warning, whereas comeau refuses to compile the following program:
#include <string>
int main()
{
&std::string("test");
}
g++ warning: taking address of temporary
comeau error: expression must be an lvalue or a function designator
Does anyone have a Microsoft compiler or other compilers and can test this program, please?
The word "shall" in the standard language means a strict requirement. So, yes, your code is ill-formed (it is an error) because it attempts to apply address-of operator to a non-lvalue.
However, the problem here is not an attempt of taking address of a temporary. The problem is, again, taking address of a non-lvalue. Temporary object can be lvalue or non-lvalue depending on the expression that produces that temporary or provides access to that temporary. In your case you have std::string("test") - a functional style cast to a non-reference type, which by definition produces a non-lvalue. Hence the error.
If you wished to take address of a temporary object, you could have worked around the restriction by doing this, for example
const std::string &r = std::string("test");
&r; // this expression produces address of a temporary
whith the resultant pointer remaining valid as long as the temporary exists. There are other ways to legally obtain address of a temporary object. It is just that your specific method happens to be illegal.
When the word "shall" is used in the C++ Standard, it means "must on pain of death" - if an implementation does not obey this, it is faulty.
It is permitted in MSVC with the deprecated /Ze (extensions enabled) option. It was allowed in previous versions of MSVC. It generates a diagnostic with all warnings enabled:
warning C4238: nonstandard extension used : class rvalue used as lvalue.
Unless the /Za option is used (enforce ANSI compatibility), then:
error C2102: '&' requires l-value
&std::string("test"); is asking for the address of the return value of the function call (we'll ignore as irrelevant the fact that this function is a ctor). It didn't have an address until you assign it to something. Hence it's an error.
The C++ standard is a actually a requirement on conformant C++ implementations. At places it is written to distinguish between code that conformant implementations must accept and code for which conformant implementations must give a diagnostic.
So, in this particular case, a conformant compiler must give a diagnostic if the address of an rvalue is taken. Both compilers do, so they are conformant in this respect.
The standard does not forbid the generation of an executable if a certain input causes a diagnostic, i.e. warnings are valid diagnostics.
I'm not a standards expert, but it certainly sounds like an error to me. g++ very often only gives a warning for things that are really errors.
user defined conversion
struct String {
std::string str;
operator std::string*() {
return &str;
}
};
std::string *my_str = String{"abc"};