Given
void foo( int&& x ) {
std::cout << &x;
}
This works but what does this address actually represent? Is a temporary int created when foo is called and that is what the address represents? If this is true and if I write int y = 5; foo(static_cast<int&&>(y));, does this cause another temporary to be created or will the compiler intelligently refer to y?
When you take an address of an rvalue reference, it returns a pointer to the object that reference is bound to, like with lvalue references. The object can be a temporary or not (if for example you cast an lvalue to rvalue reference like you do in your code).
int y = 5; foo(static_cast<int&&>(y)); does not create a temporary. This conversion is described in part 5.2.9/3 of the standard:
A glvalue, class prvalue, or array prvalue of type “cv1 T1” can be cast to type “rvalue reference to cv2 T2” if “cv2 T2” is reference-compatible with “cv1 T1”.
A temporary will be created if for example you call foo(1);.
Related
The following quotes are needed in the question:
[dcl.init.ref]/5:
5- A reference to type “cv1 T1” is initialized by an expression of
type “cv2 T2” as follows:
(5.1) [..]
(5.2) [..]
(5.3) Otherwise, if the initializer expression
(5.3.1) is an rvalue (but not a bit-field) or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or
(5.3.2) [..]
then the value of the initializer expression in the first case and the result of the conversion in the second case is called the converted initializer. If the converted initializer is a prvalue, its type T4 is adjusted to type “cv1 T4” ([conv.qual]) and the temporary materialization conversion ([conv.rval]) is applied. In any case, the reference is bound to the resulting glvalue (or to an appropriate base class subobject).
(emphasis mine)
[expr.type]/2:
If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.
Consider the following example:
const int&& r1 = 0;
Taking cv1 T1 as const int, and cv2 T2 as int
It's clear that bullet [dcl.init.ref]/(5.3.1) is applied here. The initializer expression is an rvalue (prvalue); and cv1 T1 (const int) is reference-compatible with cv2 T2 (int). And since that the converted initializer is prvalue, its type T4 (int) is adjusted to cv1 T4 (const int). Then, temporary materialization is applied.
But, per [expr.type]/2, before applying temporary materialization conversion, cv1 T4 (const int) becomes int again. Then, by applying temporary materialization, we've got an xvalue denoting an object of type int. Then the reference is bound to the resulting glvalue.
Here's my first question. The reference r1 is reference to const int and the resulting glvalue is an xvalue denoting an object of type int. So how r1, which is of type const int&&, is now binding to an xvalue of type int? Is this valid binding? Is any missing wording? Am I misunderstood/missed something?
Consider another last example:
const int&& r2 = static_cast<int&&>(0);
The same wording as above applies: The initializer expression is an rvalue (xvalue) and cv1 T1 (const int) is reference-compatible with cv2 T2 (int). And since that the converted initializer is an xvalue not prvalue, [conv.qual] or even [conv.rval] is not applied (i.e, the condition "If the converted initializer is a prvalue, ..") isn't satisfied.
I know that [conv.rval] isn't needed here since the initializer expression is already an xvalue, but [conv.qual] is required.
And that's my last question. The reference r2 is reference to const int and the resulting glvalue is an xvalue denoting an object of type int. So how r2, which is of type const int&&, is now binding to an xvalue of type int? Is this valid binding? Is any missing wording? Am I misunderstood/missed something?
Here's my first question. The reference r1 is reference to const int and the resulting glvalue is an xvalue denoting an object of type int. So how r1, which is of type const int&&, is now binding to an xvalue of type int? Is this valid binding?
Yes, that's what happens. I fail to see the issue here.
I know that [conv.rval] isn't needed here since the initializer expression is already an xvalue, but [conv.qual] is required.
No it isn't. Again, I fail to see the issue.
There is, in fact, no rule that says that a reference of type T&&, can only refer to an object whose type is exactly T. A const int&& can refer to an int object. The concept of reference-compatibility was invented in order to describe what types of objects a reference can refer to.
Given
void foo( int&& x ) {
std::cout << &x;
}
This works but what does this address actually represent? Is a temporary int created when foo is called and that is what the address represents? If this is true and if I write int y = 5; foo(static_cast<int&&>(y));, does this cause another temporary to be created or will the compiler intelligently refer to y?
When you take an address of an rvalue reference, it returns a pointer to the object that reference is bound to, like with lvalue references. The object can be a temporary or not (if for example you cast an lvalue to rvalue reference like you do in your code).
int y = 5; foo(static_cast<int&&>(y)); does not create a temporary. This conversion is described in part 5.2.9/3 of the standard:
A glvalue, class prvalue, or array prvalue of type “cv1 T1” can be cast to type “rvalue reference to cv2 T2” if “cv2 T2” is reference-compatible with “cv1 T1”.
A temporary will be created if for example you call foo(1);.
int main(int argc, char const *argv[])
{
int *i;
const int * &j = i;
}
This code gives the error error: non-const lvalue reference to type 'const int *' cannot bind to a value of unrelated type 'int *
But it's not clear to me why this is not allowed. I defined j to be a reference to a pointer to a constant int. So if I set it equal to i, aren't I saying that j is a reference of i and we are not allowed to modify the int pointed to by i through j?
So if I set it equal to i, aren't I saying that j is a reference of i
That's what you're trying to say. But the type of i is incompatible, so that's something that cannot be said.
and we are not allowed to modify the int pointed to by i through j?
If you had a pointer to const, then you couldn't modify the pointed int. And you could refer to such pointer with j. But you haven't created such pointer.
A pointer to non-const is convertible to pointer to const however. But result of such conversion is an rvalue, so your reference to non-const cannot be bound to it. If you used a reference to const, it would extend the lifetime of the temporary result of the implicit conversion:
const int * const &j = i;
But the reference just adds unncecessary cofusion. Better give the converted pointer a name, rather than refer to a temprary object:
const int* j = i;
Why is the type of i not compatible?
Because the language rules say so.
Is this just the rule?
This is the rule (from latest standard draft):
Given types “cv1 T1” and “cv2 T2”, “cv1 T1” is reference-related to “cv2 T2” if T1 is similar ([conv.qual]) to T2, or T1 is a base class of T2.
“cv1 T1” is reference-compatible with “cv2 T2” if a prvalue of type “pointer to cv2 T2” can be converted to the type “pointer to cv1 T1” via a standard conversion sequence ([conv]).
In all cases where the reference-compatible relationship of two types is used to establish the validity of a reference binding and the standard conversion sequence would be ill-formed, a program that necessitates such a binding is ill-formed.
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
If the reference is an lvalue reference and 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), where T1 is not reference-related to T2, and can be 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 ([over.match.ref]) and choosing the best one through overload resolution),
then the reference is bound to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).
Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed.
I have read that the code below is valid in C++11:
int && a = 3;
a = 4;
Is it supposed to write 4 in the memory address where the numeric literal 3 is stored? Maybe some compiler optimizations would prevent this from happening, but is it supposed to do so?
When you assign a prvalue that is not of class type to an rvalue reference, a temporary object is created and the reference is bound to that. You are simply modifying the temporary object.
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
If the reference is an lvalue reference [...]
Otherwise, [...] or the reference shall be an rvalue reference.
If the initializer expression
is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue [...], or
has a class type [...]
[...]
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.
Conceptually, a prvalue is just a value that may or may not have come from some object in memory. Literals don't have a corresponding object in memory, so this rule forces an object to be created.
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.