Somebody generalized the statement "Temporaries are rvalues". I said "no" and gave him the following example
double k=3;
double& foo()
{
return k;
}
int main()
{
foo()=3; //foo() creates a temporary which is an lvalue
}
Is my interpretation correct?
Temporaries and rvalues are different (but related) concepts. Being temporary is a property of an object. Examples of objects that aren't tempory are local objects, global objects and dynamically created objects.
Being an rvalue is a property of an expression. The opposite of rvalues are lvalues such as names or dereferenced pointers. The statement "Temporaries are rvalues" is meaningless. Here is the relationsip between rvalues and temporary objects:
An rvalue is an expression whose evaluation creates a temporary object which is destroyed at the end of the full-expression that lexically contains the rvalue.
Note that lvalues can also denote temporary objects!
void blah(const std::string& s);
blah(std::string("test"));
Inside the function blah, the lvalue s denotes the temporary object created by evaluating the expression std::string("test").
Your comment "references are lvalues" is also meaningless. A reference is not an expression and thus cannot be an lvalue. What you really mean is:
The expression function() is an lvalue if the function returns a reference.
No. You are returning a reference to an global double, not a temporary.
The same test with a real temporary would be:
double foo() { return 3.0; }
int main() {
foo() = 2.0; // error: lvalue required as left operand of assignment
}
EDIT:
The answer was meant just to identify that the example was wrong, and I did not really want to get into the deeper discussion of whether temporaries are or not rvalues... As others have said, lvalue-ness or rvalue-ness are properties of an expression and not of the object (in the most general sense, not only class instances). Then again, the standard says that:
§3.10/5 The result of calling a function that does not return a reference is an rvalue. User defined operators are functions, and whether such operators expect or yield lvalues is determined by their parameter and return types.
§3.10/6 An expression which holds a temporary object resulting from a cast to a nonreference type is an rvalue (this includes the explicit creation of an object using functional notation (5.2.3)).
Which AFAIK are the circumstances under which temporaries are created. Now, it is also true that you can bind a constant reference to a temporary, in which case you will get a new variable (the reference) that can be used as an lvalue that effectively refers to the temporary object.
The fine line is that expressions that create temporaries are rvalue expressions. You can bind a constant reference to the result of that expression to obtain a variable that can be used as an const-qualified lvalue expression.
Temporaries were so consistently protected from becoming lvalues, that they are now called rvalues. But C++0x will allow temporaries to become lvalues thanks to move semantics. Like in this dumb snippet
void blah(ICanBeTemporary && temp)
{
temp.data = 2; //here temporary becomes lvalue
}
//somewhere
blah(ICanBeTemporary("yes I can"));
Now we have terminology mess. People used to call temporaries rvalues and this is called rvalue reference. Named objects are now considered to be non-rvalue referenced.
Related
According to What are rvalues, lvalues, xvalues, glvalues, and prvalues? and some other explanations, my understanding is that xvalue is the expression which has identity and is safely moved (or is so marked).
Some texts like this and this say that, if a function f()'s return type is rvalue reference, then the expression f() is xvalue. For example:
int&& f() {
return 1;
}
int main() {
f(); // xvalue
2; // prvalue
}
My confusion is that, because the origin of f() is the literal 1, for me f() doesn't seem to have an identity and thus I can't understand how it becomes xvalue. If 1 has identity, why is 2 said to have no identity and is prvalue? Does prvalue suddenly have "identity" if it's returned from a function as an rvalue reference?
EDIT
It's pointed out that f() returns a dangling reference, but I hope my point still makes sense.
EDIT2
Well, after reading the (very helpful) comments, it seems that it probably doesn't make sense?
Does prvalue suddenly have "identity" if it's returned from a function as an rvalue reference?
Yes, actually. The standard pretty much says that outright:
[conv.rval]
A prvalue of type T can be converted to an xvalue of type T. This conversion initializes a temporary object ([class.temporary]) of type T from the prvalue by evaluating the prvalue with the temporary object as its result object, and produces an xvalue denoting the temporary object.
That temporary object, while it exists, most certainly has "identity". Of course, the result of such a conversion is no longer a prvalue, so perhaps we shouldn't say the prvalue "gets an identity." Note that this works, too, also because of temporary materialization:
(int&&)1; // This is different from f(), though, because that reference is dangling but I believe this one isn't (lifetime extension of a temporary by binding to a reference applies here but is suppressed for a return)
Note that the operand of a return statement and the thing that actually gets returned simply don't have to be the same thing. You give an int prvalue, you need an int xvalue, the return makes it work by materializing a temporary. It's not obliged to fail because of the mismatch. Unfortunately, that temporary immediately gets destroyed when the return statement ends, leaving the xvalue dangling, but, for that moment in between the returned reference being bound and the temporary being destroyed, yes, the rvalue reference indeed referred to an object with its own identity.
Other examples of prvalues being materialized so you can bind references to them:
int &&x = 1; // acts just like int x = 1 except for decltype and similar considerations
int const &y = 1; // ditto but const and also available in older C++ versions
// in some imaginary API
void install_controller(std::unique_ptr<controller> &&new_controller) {
if(can_replace_controller()) current_controller = std::move(new_controller);
}
install_controller(std::make_unique<controller>("My Controller"));
// prvalue returned by std::make_unique materializes a temporary, binds to new_controller
// might be moved from, might not; in latter case new pointer (and thus object)
// is destroyed at full-expression end (at the semicolon after the function call)
// useful to take by reference so you can do something like
auto p = std::make_unique<controller>("Perseverant Controller");
while(p) { wait_for_something(); install_controller(std::move(p)); }
Other examples of return not being trivial:
double d(int x) { return x; }
// int lvalue given to return but double prvalue actually returned! the horror!
struct dangerous {
dangerous(int x) { launch_missiles(); }
};
dangerous f() { return 1; }
// launch_missiles is called from within the return!
std::vector<std::string> init_data() {
return {5, "Hello!"};
}
// now the operand of return isn't even a value/expression!
// also, in terms of temporaries, "Hello!" (char const[7] lvalue) decays to a
// char const* prvalue, converts to a std::string prvalue (initializing the
// parameter of std::string's constructor), and then that prvalue materializes
// into a temporary so that it can bind to the std::string const& parameter of
// std::vector<std::string>'s constructor
Here I try to summarize my understanding after reading the given comments.
The whole purpose of returning an rvalue reference is to use it in some way, so returning an rvalue reference that points to a function local object, which is already invalid when the function returns, is not considered (well, I'm sure the committee does consider this of course, but not as an intended usage).
As a result, if I have a function T&& f() { /.../ return val; }, val is supposed to locate somewhere with its identity even after f() returns, otherwise it's dangling which is a mere error. Therefore, the intention that f() has an identity, so is an xvalue, is justified.
To be honest, I find the whole concept of "having identity" somewhat moot.
Here's how I tend to think about it:
A prvalue is an expression that creates an object.
An rvalue is an expression denoting a temporary object (or an object considered to be temporary, e.g. because it was std::moved).
An lvalue is an expression denoting a non-temporary object (or an object considered to be non-temporary).
A call to int &&f() {...} doesn't create a new object (at least if we ignore the function body, and only look at the function-calling mechanism itself), so the result is not a prvalue (but it's obviously an rvalue, thus it's also an xvalue).
A call to int f() {...}, on the other hand, unconditionally creates an object (the temporary int; regardless of the function body), so it's a prvalue.
Following the well accepted answer to this question Do rvalue references allow dangling references? It would seem that xvalues do not have their lifetime extended when assigned to a rvalue reference lvalue like in the question. However when I do this
#include <iostream>
using namespace std;
class Something {
public:
Something() {
cout << "Something()" << endl;
}
Something(const Something&) {
cout << "Something(const Something&)" << endl;
}
Something(Something&&) {
cout << "Something(Something&&)" << endl;
}
~Something() {
cout << "~Something()" << endl;
}
int a;
};
Something make_something() {
return Something{};
}
int main() {
auto&& something = make_something().a;
return 0;
}
The lifetime of the object returned by a call to make_something is extended, even though make_something().a is an xvalue as per http://en.cppreference.com/w/cpp/language/value_category (the third bullet in the xvalues explanation lists the member access I have above as an xvalue,)
a.m, the member of object expression, where a is an rvalue and m is a
non-static data member of non-reference type;
If value categories do not determine when the lifetime of an rvalue will be extended then what does? I am having a hard time understanding when the lifetime of an rvalue is extended in C++
Lifetime extension doesn't care about value categories. As stated by [class.temporary]/p6:
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
Emphasis added.
This says nothing here about the value category of the expression being referenced.
What determines whether a temporary is extended is exactly the above (and a few more rules).
But that does not explain why adding an std::move() around the temporary to which the reference is being assigned does not extend the lifetime
std::move is not a magical, compiler-defined construct in C++. It is a function call, and therefore it behaves no differently from any other C++ function call.
So, if you have std::move(Type()), what does that mean? It means that you will create a temporary, bind it to the parameter of std::move, then call that function, which will return something.
Binding a temporary to a function parameter, as stated in [class.temporary]/p6, means that the lifetime of the temporary is fixed to be the lifetime of the full expression that created it (if not for that rule, then the temporary would have to be destroyed at the end of the function call, since that's the end of the reference's lifetime).
It doesn't matter what the function does, says, or implies. It doesn't matter if the compiler could perhaps inline things and determine that the return value is a reference to an argument that came from a temporary. The lifetime of that temporary is fixed to the expression, not extended.
If value categories do not determine when the lifetime of an rvalue will be extended then what does? I am having a hard time understanding when the lifetime of an rvalue is extended in C++
Note that value categories describe expressions not objects. Value categories ( xvalue, prvalue, or whatever ) won't be extended in any way. Only objects can have a lifetime.
From the n4296 standard draft:
§12.2.1
Temporaries of class type are created in various contexts: binding a reference to a prvalue (8.5.3), returning
a prvalue (6.6.3), a conversion that creates a prvalue (4.1, 5.2.9, 5.2.11, 5.4), throwing an exception (15.1),
and in some initializations (8.5).
and
§12.2.4
There are two contexts in which temporaries are destroyed at a different point than the end of the full-
expression. [...]
The second context is when a reference is bound to a temporary. 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
Note: I didn't quote the first context since it has minor revelance regarding the questions. Italic emphasis added by me.
Thus the value category of the function expression makesomething() is a prvalue creating a temporary of class type, according to the first paragraph cited above.
makesomething().a accesses a temporary, complete subobject. Binding this temporary to a reference leads, according to the second context quoated above, to an extended lifetime.
The lifetime of the subobject a is coupled to the lifetime of the previous created temporary making it an expiring value (xvalue). Without extending its lifetime by binding it to a reference it would be destroyed together with the temporary class object. Thus, in this case, after the ;.
While not the actual standard, I am relying this page on cppreference.com for this specific verbiage:
An lvalue is an expression that identifies a non-temporary object or a
non-member function.
The following expressions are lvalues:
The name of a variable or function in scope, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue
reference, the expression consisting of its name is an lvalue
expression.
...
My (simplified) comprehension of the quoted section above is that an lvalue:
Is a reference
Must not be a temporary object
I know that references are not objects, so point #2 must mean that in terms of a reference, it must not refer to a temporary object. However, from the expression itself, does that mean a reference to a temporary is not an lvalue? You can have references to temporary and non-temporary objects:
int myvar = 0;
int& ref_myvar = myvar; // Reference to non-temporary
class foo {};
foo const& ref_foo = foo{}; // Reference to temporary
In the above code snippet, usage of ref_foo in a separate expression later would be an lvalue or not? There is a rule for rvalue references that states that rvalue references used by name in an expression are still lvalues (because you refer to the name at that point). Does this rule also apply to lvalue references (since they also have names and using the name itself in an expression would make it an lvalue, as it does for rvalue reference variables)?
I hope I'm making some sort of sense. At least I hope the source of my confusion is evident. Some examples of how the lvalue references above would be used in an expression to prove some points relevant to my question would be a huge help as well.
I'd say that the cppreference wording is OK for a "general introduction"-level discussion or even for "most everyday uses"-level discussion. However, once you get into the fine technical details, such statements can become somewhat misleading.
The important point is that the value category ("being an lvalue") is the property of an expression, not of an object. You can have a temporary object accessed through an lvalue, and you can have a non-temporary object accessed through an rvalue.
To refer to your examples:
ref_myvar and ref_foo are both lvalues, and always will be, regardless of how you use them. In the following:
foo&& rref = foo{};
rref is, and always will be, an lvalue as well. It is a reference to an rvalue, but the reference itself has a name and so is an lvalue.
If you want to treat an lvalue as an rvalue, you use the standard-provided case operator for that:
rvalue = std::move(lvalue);
Let's analyse this code:
int someint = std::move(ref_myvar);
ref_myvar is an lvalue. std::move(ref_myvar) is an rvalue. someint is an lvalue.
I don't think there is a concise way to define an lvalue without going full standardese, but name (or absence thereof) plays an important part in most definitions. I'll try my hand at such a definition; these are lvalues:
An expression which is a name, except for enumerators and member functions.
An expression of type "lvalue reference to something."
The result of dereferencing a pointer.
Notice that ref_myvar, ref_foo, and rref are all lvalues because they have a name. std::move(ref_myvar) doesn't have a name, and so it's an rvalue.
According to another answer, an rvalue reference will not extend the lifetime of a temporary if the expression referring to it is an xvalue expression. Since std::move returns an rvalue reference, the expression of calling it is an xvalue and so the following results in an a dangling reference:
int main()
{
std::string&& danger = std::move(get_string()); // dangling reference !
return 0;
}
That's fine. The std::move doesn't make sense here; it is already an rvalue.
But here's where I'm drawing a blank. How is this different to passing an xvalue expression as an argument, completely standard use of std::move and rvalue references?
void foo(const std::string& val);
// More efficient foo for temporaries:
void foo(std::string&& val);
int main()
{
std::string s;
foo(std::move(s)); // Give up s for efficiency
return 0;
}
Is there a special rule for rvalue reference arguments that will extend the lifetime of a temporary regardless of whether it is an prvalue or xvalue? Or is the std::move calling expression only an xvalue because we passed it something that was already an rvalue? I don't think so because it returns an rvalue reference anyway, which is an xvalue. I'm confused here. I think I'm missing something silly.
Your second example is not passing a reference to a temporary, it's passing a reference to the variable s, which lasts until the end of main().
If it were (e.g. foo(std::move(get_string()));), then the temporary's lifetime lasts until the end of the full expression - after the function has returned. It's therefore quite safe to use it within foo. There is only a danger if foo stores a reference/pointer to its argument, and something else tries to use it later.
There is no need to extend any lifetime here: the object in question lasts until the end of main, which is after the end of foo.
I'm told that, in C++03, temporaries are implicitly non-modifiable.
However, the following compiles for me on GCC 4.3.4 (in C++03 mode):
cout << static_cast<stringstream&>(stringstream() << 3).str();
How is this compiling?
(I am not talking about the rules regarding temporaries binding to references.)
I'm told that, in C++03, temporaries are implicitly non-modifiable.
That is not correct. Temporaries are created, among other circumstances, by evaluating rvalues, and there are both non-const rvalues and const rvalues. The value category of an expression and the constness of the object it denotes are mostly orthogonal 1. Observe:
std::string foo();
const std::string bar();
Given the above function declarations, the expression foo() is a non-const rvalue whose evaluation creates a non-const temporary, and bar() is a const rvalue that creates a const temporary.
Note that you can call any member function on a non-const rvalue, allowing you to modify the object:
foo().append(" was created by foo") // okay, modifying a non-const temporary
bar().append(" was created by bar") // error, modifying a const temporary
Since operator= is a member function, you can even assign to non-const rvalues:
std::string("hello") = "world";
This should be enough evidence to convince you that temporaries are not implicitly const.
1: An exception are scalar rvalues such as 42. They are always non-const.
First, there's a difference between "modifying a temporary" and "modifying an object through an rvalue". I'll consider the latter, since the former is not really useful to discuss [1].
I found the following at 3.10/10 (3.10/5 in C++11):
An lvalue for an object is necessary
in order to modify the object except
that an rvalue of class type can also
be used to modify its referent under
certain circumstances. [Example: a
member function called for an object
(9.3) can modify the object. ]
So, rvalues are not const per-se but they are non-modifiable under all but some certain circumstances.
However, that a member function call can modify an rvalue would seem to indicate to me that the vast majority of cases for modifying an object through an rvalue are satisfied.
In particular, the assertion (in the original question I linked to) that (obj1+obj2).show() is not valid for non-const show() [ugh, why?!] was false.
So, the answer is (changing the question wording slightly for the conclusion) that rvalues, as accessed through member functions, are not inherently non-modifiable.
[1] - Notably, if you can obtain an lvalue to the temporary from the original rvalue, you can do whatever you like with it:
#include <cstring>
struct standard_layout {
standard_layout();
int i;
};
standard_layout* global;
standard_layout::standard_layout()
{
global = this;
}
void modifying_an_object_through_lvalue(standard_layout&&)
{
// Modifying through an *lvalue* here!
std::memset(global, 0, sizeof(standard_layout));
}
int main()
{
// we pass a temporary, but we only modify it through
// an lvalue, which is fine
modifying_an_object_through_lvalue(standard_layout{});
}
(Thanks to Luc Danton for the code!)