Passing an integer to a function asking for reference - c++

Why is this code well-formed? I'm not passing a reference to the function:
void function(const int& ref) {
}
int main()
{
function(1);
}

Constant lvalue references can bind to rvalues. Rvalues, like your literal 1, don't have a persistent alias, so if you were to modifying it, you wouldn't be able to observe the effect, but if you promise not to modify it (namely by accessing it through a constant reference), you can still have perfectly sensible code, and that's why this binding is allowed.
(You can also bind rvalues to (mutable) rvalue references: void function(int &&) In that case, the rvalue reference becomes the (unique) alias of the value.)
Note also that without this rule it would be impossible to initialize variables from functions that return prvalues, or use copy-initialization at all:
struct T { T(int); };
T f();
T x = 1; // === "T x = T(1);", copy constructor wants to bind to prvalue
T x = f(); // ditto
T x((f())); // ditto

The compiler can create a temporary from the constant and temporaries are allowed to bind to const references. If the reference wasn't const, this wouldn't be allowed.

Related

Why is static_cast<Object&&> necessary in this function?

Trying to understand std::move, I found this answer to another question.
Say I have this function
Object&& move(Object&& arg)
{
return static_cast<Object&&>(arg);
}
What I think I understand:
arg is an lvalue (value category).
arg is of type "rvalue ref to Object".
static_cast converts types.
arg and the return type both being of type "rvalue ref to Object", the static_cast is unnecessary.
However, the linked answer says:
Now, you might wonder: do we even need the cast? The answer is: yes, we do. The reason is simple; named rvalue reference is treated as lvalue (and implicit conversion from lvalue to rvalue reference is forbidden by standard).
I still don't understand why the static_cast is necessary given what I said above.
the static_cast is unnecessary.
It may seem so, but it is necessary. You can find out easily by attempting to write such function without the cast, as the compiler should tell you that the program is ill-formed. The function (template) returns an rvalue reference. That rvalue reference cannot be bound to an lvalue. The id-expression arg is an lvalue (as you stated) and hence the returned rvalue reference cannot be bound to it.
It might be easier to understand outside of return value context. The rules are same here:
T obj;
T&& rref0 = obj; // lvalue; not OK
T&& rref1 = static_cast<T&&>(obj); // xvalue; OK
T&& rref2 = rref1; // lvalue; not OK
T&& rref3 = static_cast<T&&>(rref1); // xvalue; OK
I have the following mental model for it (let's use int instead of Object).
Objects which have a name are "sitting on the ground". They are lvalues; you cannot convert them to rvalue references.
int do_stuff(int x, int&& y) {...} // both x and y have a name
When you do calculations, you pick objects from the ground, do your stuff in mid-air and put the result back.
x + y; // it's in mid-air
do_stuff(4, 5); // return value is in mid-air
These temporary results can be converted to rvalue references. But as soon as you "put them onto the ground", they behave as lvalues.
int&& z = x + y; // on the ground
int&& z = do_stuff(6, 7); // on the ground
I am sure it only helps in simple situations, but at least it gives some real-world analogy to how C++ works.
Your first bullet is incorrect fundamentally, arg is not a lvalue, neither it is an rvalue. Neither it's a rvalue or lvalue reference, because std::move is a template. In template context a function argument of type T&& is a forwarding reference, if T is a template-parameter. Forwarding reference becomes of type which appropriate, depending on what is T.
(and implicit conversion from lvalue to rvalue reference is forbidden by standard).
A cast is required literally because of that. Following code is incorrect, because you can't call foo(v), as v is a named object and it can be an lvalue:
void foo(int && a) { a = 5; }
int main()
{
int v;
foo(v);
std::cout << a << std::endl;
}
But if foo() is a template, it may become a function with int&, const int& and int&& arguments.
template<class T>
void foo(T && a) { a = 5; }
You would be able to call foo(v+5), where argument is a temporary, which can be bound to rvalue reference. foo will change the temporary object which stops to exist after function call. That's the exact action which move constructors usually have to do - to modify temporary object before its destructor is called.
NB: An rvalue argument would cease to exist earlier , either after its use or at end of function call.
Forwarding references are a special kind of reference syntax designed to preserve the value category of a function argument. I.e. non-template function
Object&& move(Object&& arg)
is not equal to std::move for Object, which declared something like (c++11):
template<class T>
std::remove_reference<T>::type&& move( T&& t );
In non-template function arg is an lvalue, in template it have same value category as expression used to initialize it. In template std::remove_reference<T>::type refers to T, so std::remove_reference<T>::type&& is a true rvalue reference to T - a way around T&& alternative meaning.
By analogy to function call above, if implicit conversion was possible, then it would be possible to call move constructor where copy constructor is appropriate but missing, i.e. by mistake. return static_cast<Object&&>(arg); results in initialization involving call to Object::Object(Object&&) by definition of return, return arg would call Object::Object(const Object&).
Template std::move is type-correct "wrapper" around the static_cast to facilitate "implicit" cast, to simplify code by removing repeated static_cast with explicit type from code.

C++11 rvalue reference vs const reference

This may be obvious but I think it is something difficult to me. Given this:
void test(std::string&&) { }
std::string x{"test"};
test(std::move(x)); // ok
This code calls test() with a rvalue reference as parameter so the program compiles as I expect.
Now look at this:
void other_test(const std::string&) { }
std::string x{"test"};
other_test(std::move(x)); // ok???
And here I'm tilted. Why does this version compile? The std::move returns a && type; why then I don't get an error in the second method where I use const&?
I know that
int&& s = 5;
const int& s = 5;
is valid because in both cases I provide something that has not an lvalue, it has no addresses. Are && and const& equivalent? If no, are there differences?
std::move doesn't actually move anything out of it's own. It's just a fancy name for a cast to a T&&. Calling test like this test(std::move(x)); only shows that a T&& is implicitly convertible to a const T&. The compiler sees that test only accepts const T& so it converts the T&& returned from std::move to a const T&, that's all there is to it.
In simple terms:
&& can bind to non-const rvalues (prvalues and xvalues)
const && can bind to rvalues (const and non-const)
& can bind to non-const lvalues
const & can bind to rvalues (prvalues and xvalues) and lvalues (const and non-const for each). A.k.a. to anything.
If you want a function to expressly allow const-Lvalue objects, but expressly disallow Rvalue objects, write the function signature like this:
void test(const std::string&) { }
void test(std::string&&) = delete;//Will now be considered when matching signatures
int main() {
std::string string = "test";
test(string);//OK
//test(std::move(string));//Compile Error!
//test("Test2");//Compile Error!
}
test(std::string&& a) {
something(a) //--> not moved because it has lvalue
Names of variables are lvalues. a is a name of a variable, therefore a is an lvalue expression, and therefore it will not be moved from.
It's unclear what you mean by "has". a is an expression. It is a name of a reference, and references refer to objects. Value categories pertain to expressions, not objects.
test(const std::string& a): a is const lvalue reference and like before I have lvalue and rvalue. And plus more, in this case if I called
std::move(a)
where a is a const& the move works!
If by "works" you mean that it invokes a move constructor or assignment, then no, it does not work because no move construction or assignment has happened.
When you call std::move(x), an rvalue reference to the underlying data, test, will be returned. You are allowed to pass rvalue references as const (and const only!) reference parameters because an rvalue reference is implicitly convertible to a const reference. They are arguably the same thing from the function's point of view (a read only parameter). If you removed the const-qualifier of your parameter, this code would not compile:
void other_test(std::string&) { }
std::string x{"test"};
other_test(std::move(x)); //not okay because
//the function can potentially modify the parameter.
See Bo Qian's youtube video on rvalue vs lvalue.

r-value parameters in a function

I was wondering about a c++ behaviour when an r-value is passed among functions.
Look at this simple code:
#include <string>
void foo(std::string&& str) {
// Accept a rvalue of str
}
void bar(std::string&& str) {
// foo(str); // Does not compile. Compiler says cannot bind lvalue into rvalue.
foo(std::move(str)); // It feels like a re-casting into a r-value?
}
int main(int argc, char *argv[]) {
bar(std::string("c++_rvalue"));
return 0;
}
I know when I'm inside bar function I need to use move function in order to invoke foo function. My question now is why?
When I'm inside the bar function the variable str should already be an r-value, but the compiler acts like it is a l-value.
Can somebody quote some reference to the standard about this behaviour?
Thanks!
str is a rvalue reference, i.e. it is a reference only to rvalues. But it is still a reference, which is a lvalue. You can use str as a variable, which also implies that it is an lvalue, not a temporary rvalue.
An lvalue is, according to §3.10.1.1:
An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [ Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue. —end example ]
And an rvalue is, according to §3.10.1.4:
An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment
expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated with an object.
Based on this, str is not a temporary object, and it is associated with an object (with the object called str), and so it is not an rvalue.
The example for the lvalue uses a pointer, but it is the same thing for references, and naturally for rvalue references (which are only a special type of references).
So, in your example, str is an lvalue, so you have to std::move it to call foo (which only accepts rvalues, not lvalues).
The "rvalue" in "rvalue reference" refers to the kind of value that the reference can bind to:
lvalue references can bind to lvalues
rvalue references can bind to rvalues
(+ a bit more)
That's all there's to it. Importantly, it does not refer to the value that get when you use the reference. Once you have a reference variable (any kind of reference!), the id-expression naming that variable is always an lvalue. Rvalues occur in the wild only as either temporary values, or as the values of function call expressions, or as the value of a cast expression, or as the result of decay or of this.
There's a certain analogy here with dereferencing a pointer: dereferencing a pointer is always an lvalue, no matter how that pointer was obtained: *p, *(p + 1), *f() are all lvalues. It doesn't matter how you came by the thing; once you have it, it's an lvalue.
Stepping back a bit, maybe the most interesting aspect of all this is that rvalue references are a mechanism to convert an rvalue into an lvalue. No such mechanism had existed prior to C++11 that produced mutable lvalues. While lvalue-to-rvalue conversion has been part of the language since its very beginnings, it took much longer to discover the need for rvalue-to-lvalue conversion.
My question now is why?
I'm adding another answer because I want to emphasize an answer to the "why".
Even though named rvalue references can bind to an rvalue, they are treated as lvalues when used. For example:
struct A {};
void h(const A&);
void h(A&&);
void g(const A&);
void g(A&&);
void f(A&& a)
{
g(a); // calls g(const A&)
h(a); // calls h(const A&)
}
Although an rvalue can bind to the a parameter of f(), once bound, a is now treated as an lvalue. In particular, calls to the overloaded functions g() and h() resolve to the const A& (lvalue) overloads. Treating a as an rvalue within f would lead to error prone code: First the "move version" of g() would be called, which would likely pilfer a, and then the pilfered a would be sent to the move overload of h().
Reference.

Understanding reference binding

We can't bind non-const lvalue reference to an rvalue, but it can be bound to the const one. We can't bind rvalue reference to an lvalue also. Actually the Standard say so:
8.5.3/5.2:
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.
But is there a better explanation for the things than "The Standard say so"?
Because it doesn't make semantic sense.
You can't bind a non-const lvalue reference to an rvalue, because what does it mean to modify an rvalue? By definition nothing else will see the result, so this doesn't make sense.
int& i = 3;
//should change referenced location, but we aren't referencing a memory location
i = 5;
You can't bind an rvalue reference to an lvalue because rvalue references exist to facilitate destructive optimizations on their referencee. You wouldn't want your objects to be arbitrarily moved out from under you, so the standard doesn't allow it.
void process_string (std::string&&);
std::string foo = "foo";
//foo could be made garbage without us knowing about it
process_string (foo);
//this is fine
process_string (std::move(foo));
Think to some real cases:
#include <vector>
void f1( int& i ){i = 1;}
void f2( const int& i ){i = 1;}
void f3( std::vector<int>&& v ){auto v_move{v};}
int main()
{
f1(3); // error: how can you set the value of "3" to "1"?
f2(3); // ok, the compiler extend the life of the rvalue "into" f2
std::vector<int> v{10};
f3(v); // error: an innocent looking call to f3 would let your v very different from what you would imagine
f3(std::vector<int>{10}); // ok, nobody cares if the rvalue passed as an argument get modified
}

Are unnamed objects and temporary objects equivalent?

In my effort to understand rvalue references, I have been pondering when the compiler will determine that a particular function argument is an rvalue reference, and when it will determine it to be an lvalue reference.
(This issue is related to reference collapsing; see Concise explanation of reference collapsing rules requested: (1) A& & -> A& , (2) A& && -> A& , (3) A&& & -> A& , and (4) A&& && -> A&&).
In particular, I have been considering if the compiler will always treat unnamed objects as rvalue references and/or if the compiler will always treat temporary objects as rvalue references.
In turn, this leads me to question whether unnamed objects are equivalent to temporary objects.
My question is: Are unnamed objects always temporary; and are temporary objects always unnamed?
In other words: Are unnamed objects and temporary objects equivalent?
I might be wrong, since I'm not sure what the definition of "unnamed object" is. But consider the argument of the foo() function below:
void foo(int)
{ /* ... */ }
int main()
{ foo(5); }
foo()'s argument is unnamed, but it's not a temporary. Therefore, unnamed objects and temporary objects are not equivalent.
Temporary objects can be named.
Very common case - when passed as a parameter to a function.
Another less common case - binding a const reference to an rvalue result of a function.
int f(int i) { return i + 1; }
int g() { const int &j = f(1); return j; }
Unnamed objects are often temporary, but not always. For example - anonymous union object:
struct S
{
union { int x; char y; };
} s;
And, of course, any object created by operator new.
Perhaps there are other cases, but even only these can serve as counterexamples to the hypothesis :)
I have been pondering when the compiler will determine that a particular function argument is an rvalue reference, and when it will determine it to be an lvalue reference.
I assume you are talking about function templates with universal reference parameters, like this?
template<typename T>
void foo(T&& t)
{
}
The rules are very simple. If the argument is an rvalue of type X, then T will be deduced to be X, hence T&& means X&&. If the argument is an lvalue of type X, then T will be deduced to be X&, hence T&& means X& &&, which is collapsed into X&.
If you were really asking about arguments, then the question does not make much sense, because arguments are never lvalue references or rvalue references, because an expression of type X& is immediately converted to an expression of type X, which denotes the referenced object.
But if you actually meant "How does the compiler distinguish lvalue arguments from rvalue arguments?" (note the missing reference), then the answer is simple: the compiler knows the value category of every expression, because the standard specifies for every conceivable expression what its value category is. For example, the call of a function is an expression that can belong to one of three value categories:
X foo(); // the expression foo() is a prvalue
X& bar(); // the expression bar() is an lvalue
X&& baz(); // the expression baz() is an xvalue
(Provided, of course, that X itself is not a reference type.)
If none of this answers your question, please clarify the question. Also, somewhat relevant FAQ answer.