Understanding RValue-references return values

Hello,

right now I am trying to understand RValue-references as return values of functions.

First let's consider a simple function, that transforms a string into upper case letters.
1
2
3
4
5
6
7
8
const std::string
toUpper(std::string orig)
{
        std::transform(orig.begin(), orig.end(), orig.begin(), ::toupper);
        return orig;
}
...
const std::string x(toUpper("Cat"));

Will this function copy or move orig to x? Please explain to me why.

Next, we have this beauty
1
2
3
4
5
6
7
8
9
10
const std::string&&
no_sense()
{
        std::string abc("cat");
        abc = abc.substr(1, 1);
        return std::move(abc);
}
...
const std::string x(no_sense());
std::cout << x.size() << std::endl;

It compiles, but I get the output 0 .
Here I am wondering why the code above does not move the substr correctly while the code below does (prints out 1):
1
2
3
4
5
6
7
8
9
const std::string&&
no_sense(std::string abc)
{
         abc = abc.substr(1, 1);
         return std::move(abc);
}
...
const std::string x(no_sense("cat"));
std::cout << x.size() << std::endl;

In both cases abc is a temporary object inside of the function and gets deleted after the function is left. But why does the second version work and the first one does not?
cat.substr(1, 1)
And as my last question. Why doesn't
return std::move(abc.substr(1, 1));
work?

Kind regards,
Mathes
Last edited on
The best explanation I've read is here:

http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html

Long story short:

- std::move just gives you an r-value reference to the provided object. No new object is created.

- Rvalue references are still references... so whatever object you're referring to must have a lifetime that exceeds that of the reference.

Your examples don't work because 'abc' has a scope that ends when no_sense ends. Therefore your return value is a reference to an object that no longer exists. It'd be the same as if you did an l-value reference.

You can only return a reference (r-value or l-value) if the object you're referring to has lifetime longer than the function. In both your examples, abc has a lifetime limited by the function -- so they're both bad. If it's working in one of them... it's a fluke -- you're accessing garbage memory.
Last edited on
In your first code it will automatically move orig because it's a local variable inside the function, that can't be used after the function is returned, so moving it is perfectly safe.

I guess line 4 in your second code is supposed to declare abc as a std::string. Returning a reference (ordinary reference or rvalue reference) to a local variable results in undefined behaviour because the variable will no longer exist after the function has returned so you end up with a reference to a non-existing object.
Hi,

thanks for the replies. (@Peter: You are right. I fixed the missing type.)

I've read the article and your answers and now I understand why the second code (the first version of no_sense()) does not work.

But your answers and the article seem to be contradictory.

Looking at the code from the article, that is "fine C++11 code"
1
2
3
4
5
6
7
8
9
10
vector<int> doubleValues (const vector<int>& v)
{
    vector<int> new_values;
    new_values.reserve();
    for (auto itr = v.begin(), end_itr = v.end(); itr != end_itr; ++itr )
    {
        new_values.push_back( 2 * *itr );
    }
    return new_values;
}

Here it is said, that the local variable new_values will be moved after the function ends. (If I understood correctly.)
Does that mean, that my code does not work, because I explicitly use std::move(abc); on a local variable?
But if I change the code to
1
2
3
4
5
const std::string no_sense()
{
        std::string abc("cat");
        return abc.substr(1, 1);
}

it will use the move semantic automatically and work?

Kind regards,
mathes


Topic archived. No new replies allowed.