When do we practically need 'explicit xvalues'? - c++

The definition of xvalue is as follows:
— An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue reference is an xvalue. —end example ]
Will we ever fall into where we practically need to use a function whose return type is an rvalue reference, which is an xvalue?
const int && Foo()
{
// ...
}
Move semantics take an rvalue reference as a parameter, not a return value. So I don't think that's the case.

Returning rvalue references can be of use for functions that already take rvalues as parameters. A simple example:
struct X {
X() = default;
X(X&& other) { std::cout << "move ctor\n"; }
X(X const&) = delete;
void log(std::string const& s){ std::cout << "log: " << s << "\n"; }
};
void sink(X&& x) {
x.log("sink");
}
X&& passOn(X&& in) {
in.log("pass");
return std::move(in);
}
X moveOn(X&& in) {
in.log("move");
return std::move(in);
}
int main() {
sink(passOn(X()));
std::cout << "===============================\n";
sink(moveOn(X()));
}
Live demo →
The second function will call the move constructor to create the returned object, while the first will pass on the reference it already got. This is more useful if we don't return the original reference but instead a reference to a part of the referred object, e.g.
template<class T>
T&& getHead(std::vector<T>&& input) {
return std::move(input.front());
}

That's exactly what std::move is — the result of std::move execution is an xvalue. Other than that it is hard to tell since in the main returning a reference from the function is a bad thing most of the time. But maybe someone will come up with another clever usage of such a function.

Will we ever fall into where we practically need to use a function whose return type is an rvalue reference, which is an xvalue?
It used in container classes, for instance tuple has a get overload that looks like this:
template< std::size_t I, class... Types >
typename std::tuple_element<I, tuple<Types...> >::type&&
get( tuple<Types...>&& t );
I assume that std::optional and std::variant in C++17 will both have a similar overloads.
Granted, the only point is to avoid to type std::move in some very specific situations, like:
auto x = std::get<1>( f() );
Where f returns a tuple by value.

Related

(c++23 implicit move) Returning the moved local storage variable as an rvalue ref with only parenthesisses?

Regarding the proposal "Simpler implicit move" (P2266R1), I'm not sure if I understand this new "move-eligible" things correctly.
Please correct these points if incorrect:
[LIVE]
std::forward becomes optional for perfect forwarding the rvalue ref received
template<class T>
T&& seven(T&& x) { return std::forward<T&&>(x); }
becomes
template<class T>
T&& seven(T&& x) { return x; }
std::move become optional for rvalue ref created locally
Widget&&
test_seven(Widget w) {
Widget&& rr = seven(std::move(w));
return std::move(rr);
}
becomes
Widget&&
test_seven(Widget w) {
Widget&& rr = seven(std::move(w));
return rr;
}
std::move optionaly becomes parenthesis only for return an rvalue ref for things created locally.
Widget&& h3(Widget t) {
return std::move(t);
}
becomes
Widget&& h3(Widget t) {
return (t);
}
Note: (3) : clang trunk warns of returning a local stack address at the time I post this.
Update 2021-08-02
https://github.com/cplusplus/papers/issues/968#issuecomment-915353127
https://isocpp.org/files/papers/P1018R13.html#P2266r1
Poll outcome: ✅ consensus. However, the against votes are from implementors, and bring relevant new information. This topic needs to be re-discussed, and might fail a plenary vote.
All three of the points are correct. In all cases, the variable in question is an implicitly movable entity (except seven if instantiated with an lvalue) and thus is treated as an xvalue.
The parentheses here:
Widget&& h3(Widget t) {
return (t);
}
don't actually do anything. They would if the function returned decltype(auto) - since then without parentheses the function would return Widget (but still move t, not copy it).

Is there any reason to capture a return-value as an rvalue-reference?

I have a move-only struct Foo, and a function Foo get();
If I want to capture the return value of get in a mutable manner, I have two options:
by value (Foo)
by rvalue-reference (Foo&&)
When I capture by rvalue-reference I create an lvalue, much like capturing by value.
I'm struggling to see the point between the different options?
Is there any difference between these two?
Working example:
#include <iostream>
struct Foo
{
Foo(std::string s) : s(std::move(s)) {}
Foo(Foo&& f) : s(std::move(f.s)) {}
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
std::string s;
};
Foo get()
{
return Foo { "hello" };
}
int main()
{
// capture return value as l-value
Foo lv1 = get();
// move into another lvalue
Foo lv2 = std::move(lv1);
std::cout << lv2.s << '\n';
// capture return value as r-value reference
Foo&& rv1 = get();
// move into another lvalue
Foo lv3 = std::move(rv1);
std::cout << lv3.s << '\n';
return 0;
}
Foo lv1 = get();
This requires that Foo is copy/moveable.
Foo&& rv1 = get();
This does not (at least, not as far as this line of code is concerned; the implementation of get may still require one).
Even though compilers are permitted to elide the copy of the return value into the variable, copy initialization of this form still requires the existence of an accessible copy or move constructor.
So if you want to impose as few limitations on the type Foo as possible, you can store a && of the returned value.
Of course, C++17 changes this rule so that the first one doesn't need a copy/move constructor.
Foo&&
this creates a reference to a temporary value (or stores a rvalue reference assigned to it). If it stores a reference to a temporary value, its lifetime extends the value.
Foo
This stores a copy of the value, regardless of what was returned.
In C++11 and 14, if Foo cannot be moved, assigning Foo make_foo() to a variable of type Foo is illegal. There is a move there, even if the move is elided (and the return value and the value in the outer scope have merged lifetimes).
In C++17, guaranteed elision means that no move constructor need exist.
Foo x = make_foo(); // Foo make_foo()
the above in C++17 guarantees that the return value of make_foo() is just named x. In fact, a temporary within make_foo may also be x; the same object, with different names. No move need occur.
There are a few other subtle differences. decltype(x) will return the declared type of x; so Foo or Foo&& depending.
Another important difference is its use with auto.
auto&& x = some_function();
this creates a reference to anything. If some_function returns a temporary, it binds an rvalue reference to it and extends its lifetime. If it returns a reference, x matches the type of the reference.
auto x = some_function();
this creates a value, which may be copied from what some_function returns, or may be elided with the return value of some_function if it returns a temporary.
auto&& means, in a sense, "just make it work, and don't do extra work", and it could deduce to a Foo&&. auto means "store a copy".
In the "almost always auto" style, these will be more common than an explicit Foo or Foo&&.
auto&& will never deduce to Foo, but can deduce to Foo&&.
The most common uses of auto&&, even outside of almost always auto, would be:
for(auto&& x : range)
where x becomes an efficient way to iterate over the range, where we don't care what type range has much. Another common use is:
[](auto&& x){ /* some code */ }
lambdas are often used in contexts where the type is obvious and not worth typing again, like being passed to algorithms or the like. By using auto&& for parameter types we make the code less verbose.

How is the move constructor of member variable invoked without using std::forward?

An example here for std::forward,
// forward example
#include <utility> // std::forward
#include <iostream> // std::cout
// function with lvalue and rvalue reference overloads:
void overloaded (const int& x) {std::cout << "[lvalue]";}
void overloaded (int&& x) {std::cout << "[rvalue]";}
// function template taking rvalue reference to deduced type:
template <class T> void fn (T&& x) {
overloaded (x); // always an lvalue
overloaded (std::forward<T>(x)); // rvalue if argument is rvalue
}
int main () {
int a;
std::cout << "calling fn with lvalue: ";
fn (a);
std::cout << '\n';
std::cout << "calling fn with rvalue: ";
fn (0);
std::cout << '\n';
return 0;
}
Output:
calling fn with lvalue: [lvalue][lvalue]
calling fn with rvalue: [lvalue][rvalue]
mentions that
the fact that all named values (such as function parameters) always
evaluate as lvalues (even those declared as rvalue references)
Whereas, the typical move constructor looks like
ClassName(ClassName&& other)
: _data(other._data)
{
}
which looks like _data(other._data) should invoke the move constructor of _data's class. But, how is it possible without using std::forward? In other words, shouldn't it be
ClassName(ClassName&& other)
: _data(std::forward(other._data))
{
}
?
Because, as pointed out in std:forward case,
all then named values should evaluate as lvalue
I more and more like C++ because of the depth of issue like this and the fact that the language is bold enough to provide such features :) Thank you!
A typical move constructor looks like this (assuming it is explicitly implemented: you might want to prefer = default):
ClassName::ClassName(ClassName&& other)
: _data(std::move(other._data)) {
}
Without the std::move() the member is copied: since it has a name other is an lvalue. The object the reference is bound to is an rvalue or an object considered as such, however.
std::forward<T>(obj) is always used with an explicit template argument. In practice the type is that deduced for a forwarding reference. These look remarkably like rvalue references but are something entirely different! In particular, a forwarding reference may refer to an lvalue.
You may be interested in my Two Daemons article which describes the difference in detail.
std::forward should be used with a forwarding reference.
std::move should be used with an rvalue reference.
There is nothing particular about constructors. The rules apply the same to any function, member function or constructor.
The most important thing is to realize when you have a forwarding reference and when you have an rvalue reference. They look similar but are not.
A forwarding reference is always in the form:
T&& ref
for T some deduced type.
For instance, this is a forwarding reference:
template <class T>
auto foo(T&& ref) -> void;
All these are rvalue references:
auto foo(int&& ref) -> void; // int not deduced
template <class T>
auto foo(const T&& ref); // not in form `T&&` (note the const)
template <class T>
auto foo(std::vector<T>&& ref) -> void; // not in form `T&&`
template <class T>
struct X {
auto foo(T&& ref) -> T; // T not deduced. (It was deduced at class level)
};
For more please check this excellent in-depth article by Scott Meyers with the note that when the article was written the term "universal reference" was used (actually introduced by Scott himself). Now it is agreed that "forwarding reference" better describes it's purpose and usage.
So your example should be:
ClassName(ClassName&& other)
: _data(std::move(other._data))
{
}
as other is an rvalue reference because ClassName is not a deduced type.
This Ideone example should make things pretty clear for you. If not, keep reading.
The following constructor accepts Rvalues only. However, since the argument "other" got a name it lost its "rvalueness" and now is a Lvalue. To cast it back to Rvalue, you have to use std::move. There's no reason to use std::forward here because this constructor does not accept Lvalues. If you try to call it with a Lvalue, you will get compile error.
ClassName(ClassName&& other)
: _data(std::move(other._data))
{
// If you don't use move, you could have:
// cout << other._data;
// And you will notice "other" has not been moved.
}
The following constructor accepts both Lvalues and Rvalues. Scott Meyers called it "Universal Rerefences", but now it's called "Forwarding References". That's why, here, it's a must to use std::forward so that if other was an Rvalue, _data constructor will get called with an Rvalue. If other was an Lvalue, _data will be constructed with an Lvalue. That's why it's called perfect-forwarding.
template<typename T>
ClassName(T&& other)
: _data(std::forward<decltype(_data)>(other._data))
{
}
I've tried to use your constructors as an example so you could understand, but this is not specific to constructors. This applies to functions as well.
With the first example tho, since your first constructor only accepts Rvalues, you could perfectly use std::forward instead, and both would do the same thing. But it's best not to do it, because people may think that your constructor accepts a forwarding reference, when it actually doesn't.

Which function prototype is invoked (and how) when assigning a function to a std::function type?

I have the code
void prints_one()
{ cout << "one" << endl; }
int main(int argc, char *argv[])
{
std::function<void()> foo;
foo = prints_one;
foo();
return 0;
}
It works as expected; it prints "one". What I don't know is which assignment operator prototype is being invoked in the assignment and how. Looking at cpp reference, it looks like it probably is this function
template <class Fn> function& operator= (Fn&& fn);
But if that is the prototype being called, I don't understand how a function can bind to a rvalue reference. Thanks!
Update: Thanks all, I'll read up on universal references. In regards to 40two's answer; this code prints that it is an rvalue reference:
template<class Fn>
class Foo {
public:
Foo() {}
Foo& operator=(Fn&& x)
{
std::cout << std::boolalpha << std::is_rvalue_reference<decltype(x)>::value << std::endl;
}
};
void prints_one()
{
cout << "one" << endl;
}
int main(int argc, char *argv[])
{
Foo<void()> bar;
bar = prints_one;
}
This prints true
The C++ standard is meant to be confusing! Otherwise, people like me couldn't play smart just because they were watching while the C++ standard got developed. To this end it was decided that the && notation applied to types shall mean two entirely different although also confusingly related things:
When applied in a non-deduced context, i.e., with a known type T, the notation T&& means that an rvalue reference is being declared.
When applied in a deduced context, e.g., in auto&& x = ... or in function template template <typename T> void f(T&& x) the notation is to mean: determine the type and "referenceness" of the argument. That the deduced type T be of different kinds depending on what was passed as argument:
If a non-const lvalue of type X is being passed, the type T will be deduced as X&, and T&& becomes X& as a reference and an rvalue reference collapse into a reference.
If a const lvalue of type X is being passed, the type T will be deduced as X const&, and T&& becomes X const&, due to the same reference collapsing.
If an rvalue is being passed, the type T will be deduced as X, and T&& becomes X&& as there is nothing to collapse.
The motivations driving the rules on argument deduction is perfect forwarding: the argument should be forwarded to the next layer ideally look like the same kind of type as the argument which got deduced in the first places.
With this in mind, the assignment operator of std::function<...> being called is indeed
template <typename F>
std::function<...>::function(F&& f)
where the argument of the function it just deduced accordingly. In your concrete example the function pointer being passed actually is an rvalue created on the fly by decaying the function type: functions aren't really objects and can't be passed along in C++. To access them other than calling a pointer to the function is being formed. Thus, the argument is an rvalue of type void(*)().
Quoting stuff from here:
T&& isn't always an rvalue reference.
&& in a type declaration sometimes could mean an rvalue reference, but sometimes it means either rvalue reference or lvalue reference.
I'll try to contribute to the above with an example. Consider the following piece of code:
#include <iostream>
#include <type_traits>
using namespace std;
template<class F>
void foo(F&& f)
{
std::cout << std::boolalpha << std::is_rvalue_reference<decltype(f)>::value << std::endl;
}
void prints_one()
{
cout << "one" << endl;
}
int main(int argc, char *argv[])
{
foo(prints_one);
foo([](){ cout << "lambda" << endl; });
return 0;
}
If you run it the output is:
false
true
This mean that although foo takes a T&& as an input parameter, because of the fact that prints_one is an lvalue, input parameter f will be initialized with an lvalue, and consequently it will become an lvalue reference.
On the other hand in the second call of foo we pass a lambda which is really a rvalue reference. As such, the input parameter f is initialized with an rvalue and consequently it becomes an rvalue reference.
Thus, whether input parameter is deduced to an lvalue reference or an rvalue reference depends on input parameter that is passed in foo upon the time it is called.
Update:
Regarding the updated example:
You are defining a template class Foo with an assignment operator Foo<T>::operator=(T&&).
In this case T&& is not a universal reference because there's no type deduction.
It is rather an rvalue reference, hence you are getting true.
This is due to the fact that T is already deducted by the class Foo<T>. Consequently, there can't be type deduction for input parameters of member overloaded operator Foo<T>::operator=(T&&).

Correct way to return an rvalue reference to this

The code below results in Undefined Behaviour. Be sure to read ALL the answers for completeness.
When chaining an object via the operator<< I want to preserve the lvalue-ness / rvalue-ness of the object:
class Avenger {
public:
Avenger& operator<<(int) & {
return *this;
}
Avenger&& operator<<(int) && {
return *this; // compiler error cannot bind lvalue to rvalue
return std::move(*this);
}
};
void doJustice(const Avenger &) {};
void doJustice(Avenger &&) {};
int main() {
Avenger a;
doJustice(a << 24); // parameter should be Avenger&
doJustice(Avenger{} << 24); // parameter should be Avenger&&
return 0;
}
I cannot simply return *this which implies that the type of *this of an rvalue object is still an lvalue reference. I would have expected to be an rvalue reference.
Is it correct / recommended to return std::move(*this) on an member overloaded for the && qualifier, or should other method be used? I know that std::move is just a cast, so I think it’s ok, I just want to double check.
What is the reason/explanation that *this of an rvalue is an lvalue reference and not an rvalue reference?
I remember seeing in C++14 something about move semantics of *this. Is that related to this? Will any of the above change in C++14?
The type of this depends on the cv-qualifier of the member function: Avenger* or const Avenger*
But not on its ref-qualifier. The ref-qualifier is used only to determine the function to be called.
Thus, the type of *this is Avenger& or const Avenger&, no matter if you use the && or not. The difference is that the overload with && will be used then the called object is a r-value, while the & will not.
Note that rvalue-ness is a property of the expression, not the object. For example:
void foo(Avenger &x)
{
foo(x); //recursive call
}
void foo(Avenger &&x)
{
foo(x); //calls foo(Avenger &)!
}
That is, although in the second foo(), x is defined as an r-value reference, any use of the expression x is still an l-value. The same is true for *this.
So, if you want to move out the object, return std::move(*this) is The Right Way.
Could things have been different had this been defined as a reference value instead of as a pointer? I'm not sure, but I think that considering *this as an r-value could lead to some insane situations...
I didn't hear of anything changing about this in C++14, but I may be mistaken...
std::move is perhaps better called rvalue_cast.
But it is not called that. Despite its name, it is nothing but an rvalue cast: std::move does not move.
All named values are lvalues, as are all pointer dereferences, so using std::move or std::forward (aka conditional rvalue cast) to turn a named value that is an rvalue reference at point of declaration (or other reasons) into an rvalue at a particular point is kosher.
Note, however, that you rarely want to return an rvalue reference. If your type is cheap to move, you usually want to return a literal. Doing so uses the same std::move in the method body, but now it actually triggers moving into the return value. And now if you capture the return value in a reference (say auto&& foo = expression;), reference lifetime extension works properly. About the only good time to return an rvalue reference is in an rvalue cast: which sort of makes the fact that move is an rvalue cast somewhat academic.
This answer is in response to bolov's comment to me under his answer to his question.
#include <iostream>
class Avenger
{
bool constructed_ = true;
public:
Avenger() = default;
~Avenger()
{
constructed_ = false;
}
Avenger(Avenger const&) = default;
Avenger& operator=(Avenger const&) = default;
Avenger(Avenger&&) = default;
Avenger& operator=(Avenger&&) = default;
Avenger& operator<<(int) &
{
return *this;
}
Avenger&& operator<<(int) &&
{
return std::move(*this);
}
bool alive() const {return constructed_;}
};
void
doJustice(const Avenger& a)
{
std::cout << "doJustice(const Avenger& a): " << a.alive() << '\n';
};
void
doJustice(Avenger&& a)
{
std::cout << "doJustice(Avenger&& a): " << a.alive() << '\n';
};
int main()
{
Avenger a;
doJustice(a << 24); // parameter should be Avenger&
doJustice(Avenger{} << 24); // <--- this one
// Avenger&& dangling = Avenger{} << 24;
// doJustice(std::move(dangling));
}
This will portably output:
doJustice(const Avenger& a): 1
doJustice(Avenger&& a): 1
What the above output demonstrates is that a temporary Avenger object will not be destructed until the sequence point demarcated by the ';' just before the comment "// <--- this one" above.
I've removed all undefined behavior from this program. This is a fully conforming and portable program.
It is NOT ok to return std::move(*this) on a member overloaded for the && qualifier. The problem here is not with the std::move(*this) (which other answers correctly show that it is ok) but with the return type. The problem is very subtle and it was almost done by the c++11 standardization committee. It is explained by Stephan T. Lavavej in his presentation Don’t Help the Compiler during Going Native 2013. His example can be found at around minute 42 in the linked video. His example is slightly different and doesn’t involve *this and uses overload by parameter reference type rather than by method ref qualifiers but the principle is still the same.
So what is wrong with the code?
Short introduction: a reference bound to a temporary object prolongs the lifetime of the temporary object for the lifetime of the reference. That is what makes code like this be ok:
void foo(std::string const & s) {
//
}
foo("Temporary std::string object constructed from this char * C-string");
The important part here is that this property is not transitive, meaning that for the reference to prolong the lifetime of the temporary object, it must bind directly to the temporary object, and not to a reference to it.
Returning to my example:
For completness let’s add a function that takes only a const lvalue reference to Avenger (no rvalue reference overload):
void doInjustice(Avenger const &) {};
the next two calls result in UB if referencing the parameter inside the functions:
doInjustice(Avenger{} << 24); // calls `void doInustice(Avenger const &) {};`
doJustice(Avenger{} << 24); // calls `void doJustice(Avenger &&) {};`
The temporary objects constructed at parameter evaluation are destroyed as soon as the function are called for the reasons exposed above and the parameters are dangling references. Referencing them inside the functions will result in UB.
The correct way is to return by value:
class Avenger {
public:
Avenger& operator<<(int) & {
return *this;
}
Avenger operator<<(int) && {
return std::move(*this);
}
};
A copy is still eluded with the move semantics, and the return is a temporary, meaning that it will call the correct overload, but we avoid this subtle but nasty silent bug.
The example of Stephan T. Lavavej: Don’t Help the Compiler (42m–45m)
string&& join(string&& rv, const char * ptr) {
return move(rv.append(", ").append(ptr));
}
string meow() { return "meow"; }
const string& r = join(meow(), "purr");
// r refers to a destroyed temporary!
//Fix:
string join(string&& rv, const char * ptr) {
return move(rv.append(", ").append(ptr));
}
Posts on SO explaining the prolonging of life of temporary objects through references:
Does a const reference prolong the life of a temporary?
Initializing a reference
My git copy of the C++ Standard (page 290, section15.2, point 6) says:
"The exceptions to this lifetime rule [of a reference binding to a
temporary extending the lifetime of that temporary] are:
sub 9 - A temporary object bound to a reference parameter in a
function call persists until the completion of the
full-expression containing the call.
sub 10 - The lifetime of a temporary bound to the returned value
in a function return statement (9.6.3) is not extended;
the temporary is destroyed at the end of the
full-expression in the return statement. [...]"
Therefore, the implicit this `parameter' passed to
Avenger&& operator<<(int) &&
{
return std::move(*this);
}
ends its life at the ; of the statement that calls operator<< on a temporary object, even if a reference is bound to it. So this fails:
Avenger &&dangler = Avenger{} << 24; // destructor already called
dangler << 1; // Operator called on dangling reference
If OTOH the return is by value:
Avenger operator<<(int) &&
{
return std::move(*this);
}
then none of that misery happens, and there is often no extra cost in terms of copying. And indeed, if you don't offer the compiler to create that return value by move-constructing from *this, then it 'll make a copy.
So snatch *this from the brink of oblivion, and have the best of both worlds.