When a variable that is about to go out of scope is returned or thrown, its resources can be reused i.e. it can be moved from, as the following C++ program shows:
#include <iostream>
struct X {
X() {
std::cout << "X()\n";
}
X(X&&) {
std::cout << "X(X&&)\n";
}
~X() {
std::cout << "~X()\n";
}
};
X f() {
X x;
std::cout << "return\n";
return x;
}
int main() {
try {
X x = f();
std::cout << "throw\n";
throw x;
} catch (...) {
std::cout << "catch\n";
}
return 0;
}
Compilation (we turn off copy/move elision with the flag -fno-elide-constructors
), linkage, and execution:
clang++ -std=c++17 -O2 -Wall -pedantic -pthread -fno-elide-constructors\
main.cpp && ./a.out
Output:
X()
return
X(X&&)
~X()
throw
X(X&&)
~X()
catch
~X()
x
in the above statements return x;
and throw x;
denotes an object whose resources can be reused.
In the Working Draft, Standard for Programming Language C++, [basic.lval-1], we have the following definitions for value categories:
- A glvalue is an expression whose evaluation determines the identity of an object or function.
- An xvalue is a glvalue that denotes an object whose resources can be reused (usually because it is near the end of its lifetime).
- An lvalue is a glvalue that is not an xvalue.
So is x
an lvalue or an xvalue?
x
is an lvalue itself.For local variables as
x
, in return statement and throw expression, overload resolution for initialization is performed in two-phases; firstly as ifx
is an rvalue expression (then move constructor might be selected).In return statement:
In throw expression:
As the effect the move constructor is selected in both cases. This is just special for
return
andthrow
, doesn't meanx
isrvalue
orxvalue
entirely. if you write sth likeX x2(x);
inf()
, the copy constructor will be selected (and causes error because the copy constructor is implicitly-deleted).From the standard, [class.copy.elision]/3:
[expr.prim.id.unqual]/3:
[basic.lval]: