C++ pass-by-reference - c++

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.

Related

Is it well-defined to take reference of an R-value reference [duplicate]

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);.

I'm having some difficulty interpreting bullet point (5.2.1.1) in paragraph §8.5.3/5 of N4140

The snippet below compiles
#include <iostream>
int& f() { static int i = 100; std::cout << i << '\n'; return i; }
int main()
{
int& r = f();
r = 101;
f();
}
and print the values (live example)
100
101
Now, reading §8.5.3/5 in N4140, I can see that it compiles because of bullet point (5.1.1), that is, the reference is an lvalue reference, the initializer expression is an lvalue and int is reference-compatible with int (or with int& - I don't know for sure which one I should use here).
Bullet points (5.1) and (5.1.1):
— 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 ...
Now suppose I change the left value reference in the declaration int& r = f(); by a right value reference, i.e., int&& r = f();. I know the code won't compile, as an rvalue reference doesn't bind to an lvalue. But what I'm curious is, how to reach this conclusion using the Standard?
I'll explain what are my difficulties:
Clearly int&& r = f(); is covered by bullet point (5.2), because the reference is an rvalue reference.
Bullet point (5.2):
— Otherwise, the reference shall be an lvalue reference to a
non-volatile const type (i.e., cv1 shall be const), or the reference
shall be an rvalue reference.
In principle, I would say that (5.2.1.1) supports this initialization as the initializer is a function lvalue and int is reference compatible with int (or with int&).
Bullet points (5.2.1) and (5.2.1.1):
— If the initializer expression
— is an xvalue (but not a bit-field), class prvalue, array prvalue or function
lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or ...
Edit
I've included the bullet points verbatim from N4140 (C++14), which are equivalent to similar bullet points in N3337 (C++11).
the initializer expression is an lvalue and int is reference-compatible with int (or with int& - I don't know for sure which one I should use here).
Reference-compatibility is a relation applied to the type referred to, not the reference type. For example, [dcl.init.ref]/5 talks about intializing "a reference to type cv1 T1 by an expression of type cv2 T2", and later compares e.g. "where T1 is not reference-related to T2".
The type of the expression f() is just int, despite the fact that the return type of f is int&. Expressions simply do not have reference type when we observe them(*); the reference is stripped and used to determine the value category (see [expr]/5). For int& f(), the expression f() is an lvalue; for int g(), the expression g() is an rvalue.
(*)To be perfectly precise, expressions can have reference type in the Standard, but only as the "initial" resulting type. The reference is dropped "prior to any further analysis", which implies that this referencess is simply not observable through the type.
Now suppose I change the left value reference in the declaration int& r = f(); by a right value reference, i.e., int&& r = f();. I know the code won't compile, as an rvalue reference doesn't bind to an lvalue. But what I'm curious is, how to reach this conclusion using the Standard?
The confusion, as it seems from the discussion in the comments, seems to be that f() is not a function lvalue. Value categories such as "lvalue" and "rvalue" are properties of expressions. The term "function lvalue" must therefore refer to an expression, namely an expression of function type with the value category "lvalue".
But the expression f() is a function call expression. Grammatically, it's a postfix-expression, the postfix being the function argument list. As per [expr.call]/10:
A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
And [expr.call]/3
If the postfix-expression designates a destructor [...]; otherwise, the type of the function call expression is the return type of the statically chosen function [...]
That is, the (observed see above) type of the expression f() is int, and the value category is "lvalue". Note that the (observed) type is not int&.
A function lvalue is for example an id-expression like f, the result of indirecting a function pointer, or an expression yielding any kind of reference to function:
using ft = void();
void f();
ft& l();
ft&& r();
ft* p();
// function lvalue expressions:
f
l()
r()
*p()
[expr.prim.general]/8 specifies that those identifiers like f are, as id-expressions, lvalues:
An identifier is an id-expression provided it has been suitably declared. [...] The type of the expression is the type of the identifier. The result is the entity denoted by the identifier. The result is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise.
Back to the example int&& r = f();. Using some post-N4296 draft.
[dcl.init.ref]
5 A reference to type “cv1 T1” is initialized by an expression of type
“cv2 T2” as follows:
(5.1) If the reference is an lvalue reference and the initializer expression
The reference is an rvalue reference. 5.1 does not apply.
(5.2) Otherwise, the reference shall be an lvalue reference to a
non-volatile const type (i.e., cv1 shall be const), or the reference
shall be an rvalue reference. [example omitted]
This applies, the reference is an rvalue-reference.
(5.2.1) If the initializer expression
(5.2.1.1) is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and [...], or
(5.2.1.2) has a class type (i.e., T2 is a class type) [...]
The initializer is an lvalue of type int. 5.2.1 does not apply.
(5.2.2) Otherwise:
(5.2.2.1) If T1 or T2 is a class type [...]
(5.2.2.2) Otherwise, a temporary of type “cv1 T1” is created and copy-initialized (dcl.init) from the initializer expression. The reference is then bound to the temporary.
Finally, 5.2.2.2 applies. However:
If T1 is reference-related to T2:
(5.2.2.3) cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2; and
(5.2.2.4) if the reference is an rvalue reference, the initializer expression shall not be an lvalue.
T1 and T2 are int (the reference of the return type of f() is removed and used only to determine the value category), so they're reference-related. cv1 and cv2 are both empty. The reference is an rvalue reference, and f() is an lvalue, hence 5.2.2.4 renders the program ill-formed.
The reason why the term "function lvalue" appears in 5.2.1.1 might be related to the problem of "function rvalues" (see, for example, N3010 - Rvalue References as "Funny" Lvalues). There were no function rvalues in C++03, and it seems the committee didn't want to introduce them in C++11. Without rvalue references, I think it's impossible to get a function rvalue. For example, you may not cast to a function type, and you may not return function types from a function.
Probably for consistency, function lvalues can be bound to rvalue references to function types via a cast:
template<typename T>
void move_and_do(T& t)
{
T&& r = static_cast<T&&>(t); // as if moved
}
int i = 42;
move_and_do(i);
move_and_do(f);
But for T being a function type like void(), the value category of static_cast<T&&>(t) is lvalue (there are no rvalues of function type). Hence, rvalue references to function types can bind to function lvalues.

Why can a member function be called on a temporary but a global function cannot?

In the below code I call step as a member function and as a global function on a temporary value. The member function is allowed, and works, whereas the global function is disallowed due to invalid initialisation of non-const reference of type ‘kludge&’ from an rvalue of type ‘kludge’.
I'm trying to understand, from a language perspective, why one behaviour is allowed and the other is not. Technically both calls and functions seem like they'd be compiled identically, or at least could be.
#include <iostream>
struct kludge {
int a;
kludge() {
a = 1;
}
kludge & step() {
a++;
std::cout << a << ",";
return *this;
}
};
kludge get() {
kludge t;
return t;
}
kludge & step( kludge & t ) {
t.a++;
std::cout << t.a << ",";
return t;
}
int main() {
get().step();
step( get() );
}
You cannot bind rvalues to non-const lvalue references1. That applies to step(get()) as the parameter of step, which is a non-const lvalue reference, cannot be bound to the prvalue (pure rvalue) get().
However, member functions can per se be called on object arguments of every value category, be it lvalue or rvalue - [over.match.funcs]/4 and /5:
For non-static member functions, the type of the implicit object
parameter is
“lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier
[..]
For non-static member functions declared without a ref-qualifier, an
additional rule applies:
even if the implicit object parameter is
not const-qualified, an rvalue can be bound to the parameter as long
as in all other respects the argument can be converted to the type of
the implicit object parameter. [ Note: The fact that such an
argument is an rvalue does not affect the ranking of implicit
conversion sequences (13.3.3.2). — end note ]
But if you use so-called ref-qualifiers, you can restrict the value categories that are valid for a particular member function. That is, if you write:
kludge & step() & { /* .. */ }
The call get().step() will be ill-formed too.
1)
This is a well-known fact, but here is [dcl.init.ref]/5, heavily shortened:
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 [..]
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an lvalue of type “cv3 T3,”
[..]
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the
reference shall be an rvalue reference.
Temporary cannot bind to non-const reference
step( get() );
// ~~~~~ Creates a temporary object (r-value)
// But step( ) excepts a non-const reference

What does it mean to take the address of an rvalue reference?

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);.

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).