//old school '98 c++, no C++0x stuff
std::string getPath();
void doSomething()
{
const std::string &path = getPath(); //const reference to an rvalue
... // doSomething with path
}
void doSomething2()
{
const std::string path = getPath(); //regular variable
... // doSomething with path
}
what are the differences between doSomething and doSomething2 and which one is prefferable?
Is it safe to use const reference to returned rvalue in doSomething?
Does doSomething2 create a copy of returned rvalue, is compiler allowed to do rvalue optimization here?
Is it safe to use const reference to returned rvalue in doSomething?
Yes, this is perfectly fine. The language has an specific clause that guarantees that if you bind a reference to an rvalue a temporary is created and the lifetime is extended until the end of the scope where the reference is created.
Does doSomething2 create a copy of returned rvalue, is compiler allowed to do rvalue optimization here?
In both cases the cost is the same, as the compiler will do RVO (return value optimization)
what are the differences between doSomething and doSomething2 and which one is prefferable?
The main difference is that in one case you are giving a name to the real object and in the other case the name is given to a reference. The compiler should generate exactly the same code in both versions.
That being said, I find the use of the const& to be misleading. It gives the impression that the local function has a reference to some object somewhere, but it really has a copy (with no name). To realize that this is a copy the maintainer will have to look and verify the signature of the function that is being called. In the case of the value the behavior is more obvious: the function maintains a copy of whatever is returned by the function (which might be a value or reference).
The second difference is that you are only legally allowed to bind a const reference, which means that the function cannot modify the object. In the case of storing a value, the type can be non-const and the function could modify that object, so the approach is more flexible.
In C++03 the only reason to use the const& trick is in the case where you know that the function returns a value, you know that the type derives from a well known base, and you don't want to name the type [Take a look at ScopeGuard]. In C++11 that use is no longer important, as you can just use auto to let the compiler figure out the type automatically.
(from other comments it seems this might be C++ standards version dependant as to guarentees when you bind a reference to an rvalue about its lifespan - this answer reflects C++ classic)
doSomething has a reference to what?
doSomething2 has a copy of something that may not exists anymore which is fine
lets look at getPath():
in general it will take the return value, copy it to the stack and then copy that to the lhs
it can just assign directly to the lhs
it can return "hello" which will create a string on the stack - this cannot be referenced because it is temporary (the problem with doSomething)
it can return a static const string - in which case it can be completely inlined and assigned to the lhs
The reference version could have a dangling reference if the std::string was allocated inside the function getPath() and destroyed when returning from that function.
Then, it's dangerous usage.
If you had a class holding that string and the class outlives the scope of that const std::string & reference, something as MyClass::getPath(), then, in that case, the reference wouldn't be dangling.
The second version will always work.
Related
If I keep a constant reference to a non-reference returned value of a function in C++11, where does the reference point in the stack? And is it safe to do so?
string foo() {
std::string foo_ret = "foo string";
return foo_ret;
}
int main() {
const std::string& a = foo();
}
Your code is illegal; non-const lvalue references may not bind to rvalues. There's not really a good reason behind this, it's just a language rule that was introduced very early on in C++'s history.
MSVC used to (maybe still does) allow this binding, I can't comment on how MSVC implements it.
You can bind to other reference types though:
std::string const &a = foo(); // (1)
std::string&& b = foo(); // (2)
In case (2), b binds directly to the return value object, which has its lifetime extended to match b's lifetime. Note: no "move" operation occurs here, it is just binding a reference.
In case (1), conceptually, a temporary of type const std::string is initialized from the return value, and that temporary has its lifetime extended to match a's lifetime. In practice this copy will be elided. your code will behave as if the reference bound directly to the return value.
Generally speaking, you should use value semantics. std::string c = foo(); is the safest option. Because of copy elision, it is not any less efficient than the reference options.
The main danger with the reference option is that if the function were changed to return a reference, then a or b may become a dangling reference.
No it is not safe to do so, and it is an error and will not compile.
You might be looking for R Value references: http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html
In my opinion, it is legal to to do.
When you return a string from the function, it returns a Rvalue or temporary object. You can not use normal Lvalue reference to fetch the Rvalue but you can use const Lvalue reference to fetch. When you use const Lvalue reference, the life time of the temporary object is extended to be the same as the reference does.
As for where the reference point to in the memory, I guess it may vary by implementation. But what matters here is that the temporary object is no longer 'temporary', although you can not change it.
I've been studying rvalue references (a new concept for me), and am puzzled by a warning I receive in the following class function...
string&& Sampler::Serial() const {
stringstream ss;
.
. [assemble a string value using data members]
.
return ss.str();
}
This compiles successfully, but with the following warning...
..\Metrics\Sampler.cpp:71:16: warning: returning reference to temporary [-Wreturn-local-addr]
return ss.str();
^
I'm fully aware that I'm returning a temporary, as evidenced by the fact that I'm using an rvalue reference as my return type. The code seems to run fine upon execution, so why should this warrant a compiler warning?
The standard answer to similar questions seems to be to copy the return value instead of using a reference, but why should I copy potentially massive amounts of temporary data when I can move it with an rvalue reference? Isn't that why it was invented?
You're not moving your data. You're creating a local object, creating a reference to that local object, destroying that local object, and then still using that reference.
You should return by value, as you already found. But instead of copying, move the data. That's the safe way of ensuring you don't copy those massive amounts of data.
std::string Sampler::Serial() const {
std::stringstream ss;
.
. [assemble a string value using data members]
.
return std::move(ss.str());
}
Note: the std::move is technically redundant here, as ss.str() already returns an rvalue and so would already be moved. I recommend leaving it in anyway. This way works in any situation, so you don't have to think about which form to use: if you want to move, write move.
As pointed out by T.C., in general, though not in your case, this can prevent RVO. In cases where RVO is possible and where the compiler would implicitly use a move anyway, there is no need to write move explicitly. For instance:
std::string f() {
std::string x;
...
return x; // not std::move(x)
}
Here, it should already be clear to the reader that x is a local variable. It's normal for C++ code to return local variables without writing move, because either the compiler will elide the x local variable entirely and construct the std::string object directly in the return slot (whatever that means for your platform), or the compiler will use the move constructor of std::string implicitly anyway.
This is analogous to returning an lvalue reference to a local variable and puts you on the fast-track to undefined behaviour.
Regardless of whether you return an lvalue reference or an rvalue reference, you are still referencing memory which is going to be destroyed on function exit.
Rvalue reference return types should be reserved for cases when you are referencing an object which has a lifetime longer than the function, but you don't need it anymore, so are fine with it being moved-from for efficiency. For example, you might have a case in which you are temporarily storing some data, but clients can choose to "steal" the data from you. Returning an rvalue reference to the data would be reasonable in that case.
You return a (rvalue) reference to object (return by str()) of temporary object (ss) which is destroyed at end of scope.
You should return object instead:
string Sampler::Serial() const
Is it legal to have a rvalue refrence of a lvalue reference ?
Considering the following example, is there a difference between the two cases in the final rvalueRef in term of what it refer to or type (or others things)?
Type& ref = GetReference()
Type&& rvalueRef = std::move(ref)
vs
Type value = GetValue()
Type&& rvalueRef = std::move(value)
Both code is correct and 100% legal in C++. There is a difference only in the sense that in the first case a reference is moved (or better say, capable of being moved if appropriate destination is used) and in second case, it is the copy (see the example below for clarification on this).
Note that std::move does NOT actually move the object. It only casts the object into a rvalue reference type. That is all.
Also note that in both cases, rvalueRef is still an lvalue — an object which has a name (i.e an identifier to refer to) is never an rvalue. So if you don't actually move it using std::move (or using explicit cast) again, then there is no difference at all.
Here is a concrete example:
//given function
std::string& GetReference();
std::string GetValue();
//target function
void f(std::string param);
And your code follows:
std::string& ref = GetReference();
std::string&& rvalueRef = std::move(ref);
std::string value = GetValue()
std::string&& rvalueCopy = std::move(value); //named changed
till now NO DIFFERENCE at all.
But if you do this:
f(std::move(rvalueRef)); //actual object returned from GetReference is moved!
f(std::move(rvalueCopy)); //only a copy is moved.
The actual object is moved in the first case, and in the second case, the copy is moved. It doesn't make any difference at all if you do this:
f(std::move(GetReference())); //actual object returned from GetReference is moved!
f(std::move(GetValue())); //only a copy is moved.
So you see, in your code, there is no difference because there is NO ACTUAL MOVE at all. Only cast is there. To have an actual move, there should be an appropriate target type which could invoke move-constructor or move-assignment! In your case, there is NO invocation of either.
Q. Is it legal to have a rvalue refrence of a lvalue reference?
That's not really the question you are asking. The question you are really asking is this:
Q. Is it legal to cast an lvalue reference to an rvalue reference?
The answer is yes. std::move just casts its argument to an rvalue reference, which is always legal. The overall correctness of what you are doing depends on what you then do with the result of std::move. There is nothing wrong with your example code.
Q. Is there a difference between these two cases?
// case 1 // case 2
Type& ref = GetReference(); Type value = GetValue();
f(std::move(ref)); f(std::move(value));
The only difference is that in case 1, ref is a reference to the object returned by GetReference(), but in case 2, value is a copy of the object returned by GetValue().
I've got a function which returns an object of type Foo:
Foo getFoo();
I know the following will compile and will work, but why would I ever do it?
const Foo& myFoo = getFoo();
To me, the following is much more readable, and doesn't force me to remember that C++ allows me to assign an r-value to a const reference:
const Foo myFoo = getFoo();
What are the differences between the two? Why would I use the first over the second? Why would I use the second over the first?
Contrary to popular opinion, there is no guarantee that assigning the result of a function returning an object by value to a const reference will result in fewer copies than assigning it to the object itself.
When you assign an rvalue to a const reference, the compiler may bind the reference in one of two ways. It may create a new temporary by copying the rvalue and bind the reference to that, or it may bind the reference directly to the rvalue itself.
If the compiler is not able to make the 'obvious' optimization to remove the temporary and elide the copy constructor for the return value of getFoo, then how likely is it to be able to do the more efficient form of binding an rvalue to a const reference without making a new temporary?
One reason to use a const reference would be to make the function more robust against potential slicing. If the return type were actually a type derived from Foo, then assigning to a base class const reference would be guaranteed not to slice, even if the compiler did make a temporary object from the rvalue returned by the function. The compiler will also generate the correct call to the derived class destructor whether or not the destructor in the base class is virtual or not. This is because the type of the temporary object created is based on the type of the expression being assigned and not on the type of the reference which is being initialized.
Note that the issue of how many copies of the return value are made is entirely separate from the return value optimization and the named return value optimization. These optimizations refer to eliminating the copy of either the rvalue result of evaluating a return expression or of a named local variable into the return value of a function in the body of the function itself. Obviously, in the best possible case, both a return value optimization can be made and the temporary for the return value can be eliminated resulting in no copies being performed on the returned object.
I think GotW #88 answers this best
It is valid to allow this sort of pattern:
void foo(const SomeType &);
foo(SomeType(bar))
In this case, a temporary SomeType is constructed and passed to foo. Most likely, the fact that you can also have constant references on the stack to temporaries is a side effect of the verbiage used to define this behavior in the standard. Note that, as onebyone mentioned in the comments, the temporary's lifetime is extended to be that of the reference itself.
There could be several reasons:
what if you don't want a const object referred to?
For example this won't work:
const Foo &myFoo = getFoo();
myFoo.myfield = x;
Or, what if you are returning a temp object from getFoo()? This will warn about returning a reference (or address) to a local:
const Foo &getFoo(void)
{
Foo localFoo();
// do the things you want to localFoo
return( localFoo );
}
the internals of const Foo& myFoo = getFoo() do pretty much the same thing as
Foo myFoo = getFoo() so the argument that there is performance value to the const ref return are invalid. I find it no problem to return objects of reasonable size.
Disclaimer - I did not try these examples on gcc. Your mileage may vary accordingly.
One of the cool new features of the upcoming C++ standard, C++0x, are "rvalue references." An rvalue reference is similar to an lvalue (normal) reference, except that it can be bound to a temporary value (normally, a temporary can only be bound to a const reference):
void FunctionWithLValueRef(int& a) {…}
void FunctionWithRValueRef(int&& a) {…}
int main() {
FunctionWithLValueRef(5); // error, 5 is a temporary
FunctionWithRValueRef(5); // okay
}
So, why did they invent a whole new type, instead of just removing the restrictions on normal references to allow them to be bound to temporaries?
It would be pointless. You would change the thing in the function, and the change would be lost immediately because the thing was actually a temporary.
The reason for the new type stems from the need to be able to decide what actually is an rvalue and what not. Only then you can actually use them for the cool things they are used.
string toupper(string && s) { // for nonconst rvalues
for(char &c : s) make_uppercase(c);
return move(s); // move s into a returned string object
}
string toupper(string const& s) { // for the rest
// calls the rvalue reference version, by passing
// an rvalue copy.
return toupper(string(s));
}
Now, if you have some rvalue and pass it to toupper, the rvalue can directly be modified, because we know the temporary is a throw-away thing anyway, so we can aswell just change it and don't need to copy it. Also, the same observation is used for the thing called move-constructors and move-assignment. The right hand side is not copied, but its things are just stolen away and moved to *this.
If you were to say that rvalues can bind to non-const lvalue references, then you would have no way to figure out whether that references an lvalue (named object) or an rvalue (temporary) in the end.
It's probably more little know, but useful anyway, you can put lvalue or rvalue ref-qualifiers on a member function. Here is an example, which naturally extends the existing semantics of rvalue references to the implicit object parameter:
struct string {
string& operator=(string const& other) & { /* ... */ }
};
Now, you can't anymore say
string() = "hello";
Which is confusing and is not really making sense most of the time. What the & above does is saying that the assignment operator can only be invoked on lvalues. The same can be done for rvalues, by putting &&.
Because adding a new kind of reference allows you to write two overloads of a method:
void CopyFrom(MyClass &&c)
{
dataMember.swap(c);
}
void CopyFrom(const MyClass &c)
{
dataMember.copyTheHardWay(c);
}
The version that accepts the new kind of reference is allowed to modify the variable it receives, because that variable isn't going to be used anywhere else. So it can "steal" the contents of it.
This is the whole reason this feature was added; retaining one type of reference wouldn't achieve the desired goal.