what does X&& goo() mean ?

I am reading (and trying to understand) this article:
http://thbecker.net/articles/rvalue_references/section_05.html

at some point the author mentions

1
2
X&& goo();
X x = goo();


as an example of an rvalue reference that doesn't have a name.
However, I don't understand what returning X&& means and the difference is between

1
2
X&& goo()  //and
X goo()
Last edited on
I don't understand what returning X&& means

It's returning a reference. The object aliased by that reference must exist and somehow be guaranteed to outlive the call.

and the difference is between
1
2
X&& goo()  //and
X goo()


One returns by reference, the other returns by value:
1
2
3
4
5
6
7
8
9
10
11
#include <string>
#include <iostream>
std::string s;
std::string&& foo() { return std::move(s); }
std::string bar() { return s; }

int main() {
    foo() = "def";
    bar() = "abc";
    std::cout << s; // prints "def"
}


that said
Thomas Becker wrote:
"Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue."
this is a very misleading quote. Things that are declared (no matter the type) can never be l/r/x-values. Things that can be l/r/x-values can never have reference types.

Thanks, I must admit I feel I understand less than I thought.

Firstly I thought X & goo() was also a reference as in this code, which works.

1
2
3
4
5
6
7
8
9
10
11
12

#include <string>
#include <iostream>
std::string s;
std::string& foo() { return s; }
std::string bar() { return s; }

int main() {
    foo() = "def";
    bar() = "abc";
    std::cout << s; // prints "def"
}


I read that && creates a reference but to an r-value however s is an l-value so why use && to access it? I guess this is why std::move is needed, it turns the l-value into an r-value. But still, this seems convoluted compared to the &foo() approach. What am I missing?

Secondly, I thought that the argument to std::move was what was moved, not the destination and this is what this says:
https://en.cppreference.com/w/cpp/utility/move

Moreover, if return std::move(s) returns an r-value, how can "def" be assigned to it ?

Obviously, your code works but I have a hard time understanding how and deducts what the use case is for X&& goo()
Last edited on
l/r-value means the values left and right of the assignment operator=

The move semantic makes it possible to manipulate the value on the right of =
In most cases certainly unwanted. Hence you you need to do something (=> std::move()) iin order to start the move. The actually move is done by the l-value and it is and can not specified what is done in that case.

Secondly, I thought that the argument to std::move was what was moved, not the destination and this is what this says:
The value on the left side of the assignment modifies the value on the right side. && just enables this action.

Moreover, if return std::move(s) returns an r-value, how can "def" be assigned to it ?
It doesn't matter. If a value can be used as a l-value you can assign anything appropriate to it.
l/r-value means the values left and right of the assignment operator=

While that is the origin of the letters 'l' and 'r', that was only true in the long-extinct programming language CPL. It's grand-child C tried to redefine 'l' as 'locator' (that's how C standard explains the meaning of 'lvalue'), but that didn't stick.

if return std::move(s) returns an r-value, how can "def" be assigned to it ?

It's not the return statement, it's the function call expression bar() that is an r-value of type std::string. Incidentally, the function call expression foo() is also an r-value of type std::string.
Strings, like most other classes, happen to allow assignment to rvalues.

Obviously, your code works but I have a hard time understanding how and deducts what the use case is for X&& goo()

The use case was demonstrating that rvalue reference is a reference, with all that entails.
Last edited on
Thanks a lot to both of you. This really interesting but there is so much conflicting/erroneous/over-simplified information out there that is easy it easy to get confused.
easy to get confused.

true, and it makes it even harder that the concepts change over time: Bjarne's excellent "terminology.pdf" linked above is valid for C++11 and C++14, but was outdated by C++17, which totally re-wrote what 'prvalue' means.

I think it's best to focus on practical use cases rather than the tiny details that drive them and the edge cases that arise from those details

My example on this thread was not a practical use case. The only practical use case for a function returning X&& is std::move/std::forward, and I don't think there's a reason to ever write such function yourself.
In fact, Bjarne/Herb's C++ Core Guidelines agree: F.45: Don’t return a T&& http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f45-dont-return-a-t
Last edited on
Topic archived. No new replies allowed.