c++ return rvalue as const left ref - c++

Since we can pass rvalue to function taking const left ref,
void taking(const string& ref) {}
taking("abc");
can we return rvalue as const left ref without reporting warning?
const string& returning()
{
static string s = "abc";
if (1)
{
return s;
}
else
{
return "xyz"; // warning: return-local-addr
}
}
cout<<returning()<<endl;

The issue is not the value category (lvalue vs rvalue), but the fact that you return a reference at all.
It doesn't matter what kind of reference. When the function returns, the object that the return value is referencing, will be gone. In the case of return s; that is the local variable and in the case of return "xyz"; it is the temporary constructed in the return statement (which must be constructed because "xyz" itself is not a string and so cannot be bound to const string&).
You must return-by-value:
string returning()
If you do so, then an expression of the form returning() will be a prvalue (a kind of rvalue) and can be used e.g. in a function expecting a const lvalue reference, which can also bind to rvalues.
taking(returning()) // ok!
This is ok, because the return value of the function itself is not destroyed when the function exits (that would be nonsensical). Instead it lives until the end of the full expression in which the function was called.
There are also no unnecessary copies of the object being made due to copy elision rules (except for an unlikely extra move operation in return s;).

Related

Returning const reference to temporary

Why does the function foo gives the warning "returning reference to temporary"
const bool& foo()
{
return true;
}
if declaring bar like this is fine and doesn't generate any kind of warning
const bool& bar = true;
PS: i'm using GCC
The 2nd one is fine because the lifetime of temporary bool constructed from true will be extended to the lifetime of the reference bar.
Whenever a reference is bound to a temporary object or to a subobject thereof, the lifetime of the temporary object is extended to match the lifetime of the reference
But the lifetime rule doesn't work for the 1st case.
There are following exceptions to this lifetime rule:
a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of
the return expression. Such return statement always returns a dangling
reference.
So foo always returns a dangling reference.
songyuanyao answered with what the standard says about it. But that doesn't really explain why they decided c++ should behave this way.
It might be easier to think about the code if you think about what the compiler makes of it:
const bool& bar = true;
The lifetime of the temporary is extended to the lifetime of bar so this code is equivalent to:
const bool temp = true;
const bool& bar = temp;
But lets apply the same to the function return:
const bool& foo()
{
return true;
}
becomes
const bool& foo()
{
const boot temp = true
return temp;
}
Does this make it clearer that you are now returning a reference to an object that gets destroyed? The lifetime of temp can't be extended past the end of the function because 1) the storage location is in the stack frame of fooand that goes away, 2) the caller might have no idea the function returns something with extended lifetime it would have to destroy later, it might only see the function declaration const bool& foo().
So if the compiler could extend the lifetime past the end of the function then it would leak memory.

Is it allowed to return a moved object as a const lvalue reference?

Is it allowed to return a moved value as a const lvalue reference?
include<string>
using namespace std;
class C {
private:
string s;
public:
const string &release() {
return move(s);
}
};
Well, yes but it won't do anything.
The std::move function is just a cast to a rvalue reference. So in effect:
std::string s;
std::move(s); // returns std::string&& to `s`
So it just returns a reference to the object you pass in.
So in your code, you create an rvalue reference to your string s, but you bind that reference to a std::string const&, which cannot be moved from.
You'd be better simply returning the reference directly:
const string &release() {
return s;
}
Or return by move (using exchange):
std::string release() {
return std::exchange(s, std::string{});
// s valid to be reused, thanks to std::exchange
}
The last solution would be to return an rvalue reference, but I wouldn't do that, as it won't guarantee the reference to be moved from.
No, and I don't see why you would want to. std::move is like a cast, to the rvalue-reference type. The point of rvalue references returned from std::move is for their corresponding values to be moved into another object efficiently, possibly changing the original object in the process. A const lvalue reference would not allow you to change the object, so it can't be moved from.

C++ temporary variable lifetime?

I know this is valid in C++, because temporary from std::string("xxx") lives to the end of the full expression it appears in.
printf("%s\n", std::string("xxx").c_str());
And this is valid too:
std::string f1() {
return std::string("xxx");
}
void g1() {
printf("%s\n", f1().c_str());
}
But is this valid? if not, why?
const char* f2() {
return std::string("xxx").c_str();
}
void g2() {
printf("%s\n", f2());
// or: std::string x = f2(); // is this valid?
}
The temporary std::string inside of f2 is destroyed when the function returns. The char* will be dangling.
EDIT: In response to the comment, std::string x = f2() is also not valid. It's still bad to initialize a std::string from a dangling pointer.
The exact same rule applies. The full-expression in which the std::string was created in f2 is
std::string("xxx").c_str()
together with the construction of the return value from it.
After this expression executed and the construction of the return value executed, the temporary std::string("xxx") will be destroyed. Since destruction of the temporary std::string makes the const char* obtained from .c_str() and copied into the return value invalid, the return value of the function cannot be used in any way.
For f1 the same applies (details relating to temporary materialization and copy elision, especially in C++17, aside). The temporary std::string is destroyed after construction of the return value. But in this case this is not a problem, because the return value is not a pointer to something that was destroyed, but an independent std::string object itself. This std::string object will live until the end of the full-expression in which the call to f1 appears, i.e. until the end of
printf("%s\n", f1().c_str())
The following would be fine as well:
std::string f3() {
return std::string("xxx").c_str();
}
Here again, a new std::string is created in the return value, which will live until the end of the full-expression in which f3 is called.
In practice, for f1, since std::string("xxx"); is already a prvalue of type std::string, copy elision will be applied, so that there is really only one temporary object, the return value of f1. The intermediate temporary will be eliminated and the return value directly constructed from "xxx". This is mandatory since C++17 and was allowed and common before C++17.

Const reference doesn't prolongs the life of temp due to RVO?

This is follow up of question Is there a sequence point between return and expression in return statement? . The answer-er isn't replying to my comments , neither I'm unable to understand from his answer, nor I know how to bump the thread. So I created this question, my sincere apologies for doing this.
Consider the below code :
#include <iostream>
using namespace std;
struct foo{const char* bar ; foo(): bar("This is foo"){} };
foo returnByValue(){ return foo(); }
const foo& returnByConstRef() { return returnByValue(); } // *
int main() {
std::cout<< returnByConstRef().bar <<std::endl; // is life of temp is extended in while this expression?
return 0;
}
My understanding is that returnByValue() expression (inside returnByConstRef()) is the copy of temporary object foo() (using copy ctor) . Now returnByConstRef() which is const reference to temp object returned by returnByValue() (copy of original temp() object in the code) , Now when I invoke returnByConstRef().bar why is it undefined behavior ?
Where is my thinking wrong? , does RVO does this?
This isn't due to RVO, the standard specifies that temporaries bound to returned values do not have their lifetime extended:
N3337 [class.temporary]/5: [...] The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
[...]
The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not
extended; the temporary is destroyed at the end of the full-expression in the return statement.
[...]
For your example:
const foo& returnByConstRef()
{
return returnByValue();
}
returnByValue() is a temporary which is being bound to the return value, kind of like this:
const foo& returnByConstRef()
{
const foo& _temporary_object = returnByValue();
return _temporary_object;
//object referenced by _temporary_object is destroyed
}
Such temporaries do not have their lifetime extended; they are destroyed on function exit. Maybe it would help you to think of it like this:
function returnByConstRef
call returnByValue, put value on the stack
put pointer to value in return register
clear up stack, invalidating pointer to the value

Returning reference to temporary (built in types)

From this thread it is clear that a string literal cannot be used for a function that returns const string& since there is an implicit conversion from const char* to std::string which creates a temporary.
But then why do I get a warning "warning: returning reference to a temporary" if my return type match exactly and there is no need for conversion, e.g.:
include <iostream>
const int& test(){
return 2;
}
int main(){
std::cout << test();
}
No implicit conversion needed to happen on the return value of 2, so why is there a warning? I thought using test() would be pretty much the same as doing
const int& example = 2;
which is perfectly valid. Additionally if I change 2 to 2.2 (so it is a double) the program still runs (with the same warning) despite the fact there IS a conversion from double to int? Shouldn't I be running in to an issue similar to how const char* was returned to the string reference, if there is a conversion from double to int?
A temporary is still created. §8.5.3/(5.2.2.2) applies1:
Otherwise, a temporary of type “ cv1 T1” is created and
copy-initialized (8.5) from the initializer expression. The reference
is then bound to the temporary.
This also applies in your second example. It does not apply for prvalues of class type, or scalar xvalues: Both
const A& a = A();
// and
const int& i = std::move(myint);
do not introduce a temporary. However, that isn't changing the final result : In any case, the temporary that is bound to the reference will be destroyed at the end of the return statement - §12.2/(5.2):
The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary
is destroyed at the end of the full-expression in the return
statement.
That is, the temporary is destroyed before the function even exits, and thus your program induces undefined behavior.
1 I could go on and quote the entire list to show why it does, but that would presumably be a waste of answer space.
an implicit conversion from const char* to std::string which creates a temporary
You are conflating the two topics. Implicit conversions are not the only thing that can create temporaries:
const std::string& test() {
return std::string("no implicit conversions here.");
}
There is no implicit conversion to create the string, but the created string is still temporary, and so you're still returning a reference to a temporary object.
In your example, 2 is still a temporary value, temporarily stored somewhere on the stack, and whose location/address is returned so callers can get at the value, but they are not supposed to.