Understanding Rvalue Reference - c++

Is this undefined behavior? If not, what is the behavior?
// In some external library with, say, header "a.h"
void f(int &&x) {
x = 5; // Which memory does this assignment apply to?
}
#include "a.h"
int main() {
f(7);
// At this point, where is the value of 5?
return 0;
}

C++11 8.5.3 describes initialization of references (including rvalue references).
It says:
Otherwise, a temporary of type “cv1 T1” is created and initialized
from the initializer expression using the rules for a non-reference
copy-initialization (8.5). The reference is then bound to the
temporary.
So, a temporary of type int is bound to the rvalue reference, and thrown away immediately after the call returns.
Section 12.2 (Temporaries) gives examples very similar to yours.

Related

const reference to temporary reference

#include <iostream>
using namespace std;
struct CL
{
CL()
{
cout<<"CL()"<<endl;
}
CL(const CL&)
{
cout<<"CL(const CL&)"<<endl;
}
~CL()
{
cout<<"~CL()"<<endl;
}
};
CL cl;
CL fnc()
{
return cl;
}
int main() {
cout<<"start"<<endl;
const CL& ref=static_cast<const CL&>(fnc());
//...Is "ref" valid here??
cout<<"end"<<endl;
return 0;
}
What's lifetime of temporary object returned by fnc()? Is it lifetime of "ref" or of temporary reference static_cast(fnc()), which destroyed at end of statement?
Output of gcc (lifetime of fnc() is lifetime of "ref"):
CL() //global object "cl"
start
CL(const CL&)
end
~CL()
~CL() //global object "cl"
Output of VS2013 (lifetime of fnc() is lifetime of temporary reference):
CL() //global object "cl"
start
CL(const CL&)
~CL()
end
~CL() //global object "cl"
What's correct by Standard?
I believe Visual Studio is correct here, this is covered in defect report #1376 which says:
In a declaration like
T&& r = static_cast<T&&>(T());
it is not clear what the lifetime of the T temporary should be.
According to 5.2.9 [expr.static.cast] paragraph 4, the static_cast is
equivalent to a declaration of an invented temporary variable t. The
lifetime of the temporary is extended to that of t, but it is not
clear what that lifetime should be, nor if the subsequent binding of t
to r would affect the lifetime of the original temporary. (See also
issue 1568.)
and the discussion includes this conclusion:
The reference is bound to the xvalue result of the static_cast, so the lifetime of the temporary is not extended and this example results in a dangling reference.
and defect report 1568 covers this case more specifically:
According to 12.2 [class.temporary] paragraphs 4-5,
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...
It is not clear whether this applies to an example like the following:
struct S { };
const S& r = (const S&)S();
and the response was:
This issue is a duplicate of issue 1376.
so in this case:
const CL& ref=static_cast<const CL&>(fnc());
the reference is bound to the result of the static_cast and not to CL and therefore CL is a dangling reference.
For reference the relevant text from the draft C++11 standard section 5.2.9 [expr.static.cast]:
Otherwise, an expression e can be explicitly converted to a type T using a static_cast of the form static_-
cast(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (8.5). The
effect of such an explicit conversion is the same as performing the declaration and initialization and then
using the temporary variable as the result of the conversion. The expression e is used as a glvalue if and
only if the initialization uses it as a glvalue.

Life extension of temporary by const reference

C++
I'm trying to see how const references prolong the lifetime of temporaries. I'm running the code from the snippet in one of the answers to What are the differences between pointer variable and reference variable in C++? and got conflicting results between VC11 and g++ 4.8. I've expanded the snippet here:
#include <stdio.h>
struct scope_test
{
~scope_test() { printf("scope_test done!\n"); }
};
int main()
{
const scope_test& test = scope_test();
printf("in scope\n");
}
The answerer got the result:
in scope
scope_test done!
I tried it in VC11 and got this:
scope_test done!
in scope
scope_test done!
I assumed the VC11 result was caused by a lack of copy elision, so I tried to see if disabling copy elision on g++ with fno-elide-constructors would give the same result as VC11. (I don't think copy elision can be toggled in VC11.) But, g++ gives the answerer's result regardless of the setting of the flag.
The C++11 Standard, ISO/IEC 14882:2011(E), §12.2/4 and /5 states:
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 except:
...
Does the VC11 result have anything to do with copy elision? Is it a VC11 bug?
The answerer states:
temporaries assigned to const references live until the const
reference goes out of scope
The list of exceptions to §12.2/5 don't rule out a non-const reference. What am I missing from the Standard?
Removing the const in VC11 produces the same result as the VC11 one with the const. Removing the const in g++ gives error: invalid initialization of non-const reference of type ‘scope_test&’ from an rvalue of type ‘scope_test’. Why is there a difference?
EDIT:
I added copy and move constructors and tried:
#include <stdio.h>
struct scope_test
{
scope_test() { printf("regular ctor\n"); }
scope_test(const scope_test& src) { printf("copy ctor\n"); }
scope_test(scope_test&& src) { printf("move ctor\n"); }
~scope_test() { printf("scope_test done!\n"); }
};
int main()
{
const scope_test& test= scope_test();
printf("in scope\n");
}
Regardless of the toggling of copy elision, g++ gives:
regular ctor
in scope
scope_test done!
VC11 gives the same thing, even if the const is removed. If the const is removed from g++, g++ still gives error: invalid initialization of non-const reference of type ‘scope_test&’ from an rvalue of type ‘scope_test’.
Both behaviors are correct, certainly according to the C++03 standard (8.5.3 [dcl.init.ref] paragraph 5):
Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const). [Example: ...]
If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation-defined):
— The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object.
— A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.
I think the definition of C++11 still allows the copy to be made but the wording doesn't as clearly allow the copy. In any case, VC++ doesn't claim to be fully C++11 compliant.

C++ pass-by-reference

I'm trying C++ pass-by-reference using this simple code:
#include <iostream>
int square(int &x)
{
return x*x;
}
int main()
{
std::cout<<"Square of 4 is: "<<square(4)<<std::endl;
return 0;
}
But, when I try to run it, I get the following:
UPDATE
I get the following error after modifying the code based on #Pablo Santa Cruz's answer (I just screen captured part of the error):
Why is that?
Thanks.
You cannot pass temporaries to non-constant references.
Make square accept a const int &:
int square(const int &x)
{
return x*x;
}
You can't pass a constant (4) if you are expecting a reference.
You must pass a variable:
int n = 4;
square(n);
Having said that, you probably want something like this:
void square(int &x)
{
x = x*x;
}
Not a function returning an int.
It's because of § 8.5.3 5 of C++03:
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
If the initializer expression
is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or
has a class type (i.e., T2 is a class type) and can be implicitly converted to an lvalue of type “cv3 T3,” where “cv1 T1” is reference-compatible with “cv3 T3” 92) (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6) and choosing the best one through over- load resolution (13.3)),
then the reference is bound directly to the initializer expression lvalue in the first case, and the reference is bound to the lvalue result of the conversion in the second case. In these cases the reference is said to bind directly to the initializer expression. [Note: the usual lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are not needed, and therefore are suppressed, when such direct bindings to lvalues are done. ]
Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const).
If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation-defined.
The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object.
A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.
Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy initialization (8.5). The reference is then bound to the temporary. If T1 is reference-related to T2, cv1 must be the same cv-qualification as, or greater cv- qualification than, cv2; otherwise, the program is ill-formed.
Above, "cv*" refers to the modifiers "const" and "volatile", and "T*" refer to type names. For example, const int (cv = "const", T = "int"), const volatile std::string (cv = "const volatile", T = "std::string"), char* (cv = "", T = "char*").
In short, rvalues are allowed to bind only to const references.
Update
If your square now returns void (code or it didn't happen), then the new error is because there is no operator<<(std::out&, void).
Compiler creates a temporary object for constant 4 which can not be passed to a function as a non-const reference. Create a int object in main and pass it to the function or take the function parameter by const-reference or by copy.
The declaration
int square(int& x);
says that the function square may change its argument (although it doesn't really). So to call square(4) is ridiculous: the program needs to be ready to change the number 4.
As people have noted, you can either change the function to specify that it won't change its argument:
int square(const int& x); // pass reference to const type
// OR
int square(int x); // pass by value
Or you can call your original square using a value that can be modified.
int square(int& x);
// ...
int num = 4;
square(num);
// now (as far as the compiler knows) num might no longer be 4!
A reference must be an l-value-- basically, something you can see on the left side of an assignment statement.
So basically, you need to assign to a variable in main first. Then you can pass it into square.
A reference aliases another variable. "4" is not a variable, and therefore your reference has nothing to alias. The compiler therefore complains.
What you want is this:
#include <iostream>
int square(int x)
{
return(x * x);
}
int main()
{
std::cout << "Square of 4 is: "<< square(4) << std::endl;
return 0;
}
Did you notice how I got rid of the & in int square(int &x)? The & means pass-by-reference. It takes a variable. The 4 in square(4) is a constant.
Using pass-by-reference, you can change the value of x:
void square2(int &x)
{
x = x * x;
return;
}
Now:
int x = 5;
square2(x);
// x == 25
Although, I don't think you want this. (Do you??) The first method is a lot better.

Function takes a reference parameter with a default value

Based on
http://www.cplusplus.com/reference/stl/vector/vector/
explicit vector ( const Allocator& = Allocator() );
This vector constructor takes a reference parameter which has default value of Allocator(). What I learn from this function signature is that a function can take a reference parameter with default value.
This the demo code I play with VS2010.
#include "stdafx.h"
#include <iostream>
using namespace std;
void funA(const int& iValue=5) // reference to a template const int 5 why?
{
cout << iValue << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
funA();
funA(10);
return 0;
}
are there some rules to guide this syntax usage (i.e. a reference parameter with a default value)?
Const references may be bound to temporary objects, in which case the lifetime of the temporary extends to the lifetime of the reference.
The only rules I can think of are (a) that the reference must be const, because you can't bind a non-const reference to a temporary, and (b) that it's generally better not to use const references to pass built-in types. In other words:
(a)
void f(T& t = T(23)) {} // bad
void g(const T& t = T(23)) {} // fine
(b)
void f(const int& i = 23) {} // sort of ok
void g(int i = 23) {} // better
This behavior is defined in § 8.3.6 5 of c++03:
A default argument expression is implicitly converted (clause 4) to the parameter type. The default argument expression has the same semantic constraints as the initializer expression in a declaration of a variable of the parameter type, using the copy-initialization semantics (8.5).
That is, const Type& var = val is a valid parameter declaration only if it's also a valid variable declaration. According to § 8.5.3 5, it is. For const Allocator& = Allocator(), the following applies:
Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const). [...]
If the initializer expression is an rvalue, with T2 a class type, and "cv1 T1" is reference-compatible with "cv2 T2," the reference is bound in one of the following ways (the choice is implementation defined):
The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object.
A temporary of type "cv2 T2" [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.
The constructor that would be used to make the copy shall be callable whether or not the copy is actually done. [...]
Otherwise, [...]
For const int& iValue=5, the next case applies:
Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const). [...]
If the initializer expression is an rvalue[...]
Otherwise, a temporary of type "cv1 T1" is created and initialized from the initializer expression using the rules for a non-reference copy initialization (8.5). The reference is then bound to the temporary. If T1 is reference-related to T2, cv1 must be the same cv-qualification as, or greater cv-qualification than, cv2; otherwise, the program is ill-formed. [Example:
const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0
const volatile int cvi = 1;
const int& r = cvi; // error: type qualifiers dropped
---end example]
In short, a real, though perhaps temporary, variable is created so the reference can refer to it. It's allowed in parameter declarations exactly so that reference parameters can take default values. Otherwise, it would be a needless restriction. The more orthogonal a language is, the easier it is to keep in your head, as you don't need to remember as many exceptions to the rules (though, arguably, allowing const references but not non-const references to be bound to rvalues is less orthogonal than disallowing any reference to be bound to an rvalue).

Is a member of an rvalue structure an rvalue or lvalue?

A function call returning a structure is an rvalue expression, but what about its members?
This piece of code works well with my g++ compiler, but gcc gives a error saying "lvalue required as left operand of assignment":
struct A
{
int v;
};
struct A fun()
{
struct A tmp;
return tmp;
}
int main()
{
fun().v = 1;
}
gcc treats fun().v as rvalue, and I can understand that.
But g++ doesn't think the assignment expression is wrong. Does that mean fun1().v is lvalue in C++?
Now the problem is, I searched the C++98/03 standard, finding nothing telling about whether fun().v is lvalue or rvalue.
So, what is it?
A member of an rvalue expression is an rvalue.
The standard states in 5.3.5 [expr.ref]:
If E2 is declared to have type
“reference to T”, then E1.E2 is an
lvalue [...]
- If E2 is a non-static data member, and the type of E1 is “cq1 vq1 X”, and
the type of E2 is “cq2 vq2 T”, the
expression designates the named member
of the object designated by the first
expression. If E1 is an lvalue, then
E1.E2 is an lvalue.
This is a good time to learn about what xvalues an glvalues are.
Rvalues can be of two types - prvalues and xvalues. According to the new C++17 standard
A prvalue is an expression whose evaluation initializes an object, bit-field, or operand of an operator, as specified by the context in which it appears.
so something like fun() in your example evaluates to an prvalue (which is an rvalue). This also tells us that fun().v is not a prvalue, since it is not a vanilla initialization.
Xvalues which are also rvalues are defined like so
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). Certain kinds of expressions involving rvalue references (8.3.2) yield xvalues. [ Example: The result of calling a function whose return type is an rvalue reference to an object type is an xvalue (5.2.2). - end example ]
In addition to rvalues, another umbrella value category is a glvalue which be of two types xvalues and the traditional lvalues.
We have at this point defined the essential value categories. This can be visualized like so
The category glvalue can broadly be thought to mean what lvalues were supposed to mean before move semantics became a thing - a thing that can be on the left hand side of an expression. glvalue means generalized lvalue.
If we look at the definition of an xvalue, then it says something is an xvalue if it is near the end of its lifetime. In your example, fun().v is near the end of its lifetime. So its resources can be moved. And since its resources can be moved it is not an lvalue, therefore your expression fits in the only leaf value category that remains - an xvalue.
Edit: Ok, I guess I finally have something from the standard:
Note that v is of type int which has an built-in assignment operator:
13.3.1.2 Operators in expressions
4 For the built-in assignment operators, conversions of the left operand are restricted as follows:
— no temporaries are introduced to hold the left operand, and [...]
fun1() should return a reference. A non-reference/pointer return type of a function is a r-value.
3.10 Lvalues and rvalues
5 The result of calling a function that does not return an lvalue reference is an rvalue [...]
Thusly, fun1().v is a rvalue.
8.3.2 References
2 A reference type that is declared
using & is called an lvalue reference,
and a reference type that is declared
using && is called an rvalue
reference. Lvalue references and
rvalue references are distinct types.
I've noticed that gcc tends to have very few compunctions about using rvalues as lvalues in assignment expressions. This, for example, compiles just fine:
class A {
};
extern A f();
void g()
{
A myA;
f() = myA;
}
Why that's legal and this isn't (i.e. it doesn't compile) though really confuses me:
extern int f();
void g()
{
f() = 5;
}
IMHO, the standard committee has some explaining to do with regards to lvalues, rvalues and where they can be used. It's one of the reasons I'm so interested in this question about rvalues.
It becomes obvious when you consider that the compiler will generate a default constructor, a default copy constructor, and a default copy assignment operator for you, in case your struct/class does not contain reference members. Then, think of that the standard allows you to call member methods on temporaries, that is, you can call non-const members on non-const temporaries.
See this example:
struct Foo {};
Foo foo () {
return Foo();
}
struct Bar {
private:
Bar& operator = (Bar const &); // forbid
};
Bar bar () {
return Bar();
}
int main () {
foo() = Foo(); // okay, called operator=() on non-const temporarie
bar() = Bar(); // error, Bar::operator= is private
}
If you write
struct Foo {};
const Foo foo () { // return a const value
return Foo();
}
int main () {
foo() = Foo(); // error
}
i.e. if you let function foo() return a const temporary, then a compile error occurs.
To make the example complete, here is how to call a member of a const temporarie:
struct Foo {
int bar () const { return 0xFEED; }
int frob () { return 0xFEED; }
};
const Foo foo () {
return Foo();
}
int main () {
foo().bar(); // okay, called const member method
foo().frob(); // error, called non-const member of const temporary
}
You could define the lifetime of a temporary to be within the current expression. And then that's why you can also modify member variables; if you couldn't, than the possibility of being able to call non-const member methods would be led ad absurdum.
edit: And here are the required citations:
12.2 Temporary objects:
3) [...] Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. [...]
and then (or better, before)
3.10 Lvalues and rvalues:
10) 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. ]
And an example use: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Named_Parameter
You code has no scene. Returned structure is allocated on stack, so assignment result is immediately will be lost.
Your function should eiter allocate new instance of A by:
new A()
In this case better signature
A* f(){ ...
Or return existing instance, for example:
static A globalInstance;
A& f(){
return globalInstance;
}