Overload resolution between const lvalue reference and rvalue reference - c++

#include <iostream>
#include <string>
void fnc (const std::string&)
{
std::cout<<1;
}
void fnc (std::string&&)
{
std::cout<<2;
}
int main()
{
fnc ("abc");
}
All the compilers choose std::string&& version of fnc, and it is logical, because the temporary std::string is created for a reference binding, but I can't find, where is it described in C++ 14 Standard.
I found one paragraph in there (3.2):
— Standard conversion sequence S1 is a better conversion sequence than
standard conversion sequence S2 if
[...]
— S1 and S2 are reference bindings (8.5.3) and neither refers to an
implicit object parameter of a non-static member function declared
without a ref-qualifier, and S1 binds an rvalue reference to an rvalue
and S2 binds an lvalue reference
But it isn't that case, because S1 binds an rvalue reference to an lvalue ("abc", lvalue of const char[4]).
Where can I find description, by which the second overload is selected?
P.S. I pointed to C++14 Standard instead of C++11, because I know, that there was some defect reports in C++11, linked with rvalue reference binding.

First the compiler performs an implicit array-to-pointer conversion for "abc", so the type of "abc" becomes const char*. Second (and you probably missed that), const char* is converted to a rvalue std::string via the const char* non-explicit constructor of std::string (# 5 in the link). The constructed std::string rvalue is a perfect match for the second overload, so the second overload is chosen.

But it isn't that case, because S1 binds an rvalue reference to an lvalue ("abc", lvalue of const char[4]).
Note that "abc" is a const char[4], not a std::string. But both fnc() take std::string as parameter, and references can't be bound to objects with different type directly. Therefore firstly "abc" needs to be implicitly converted to std::string, which is a temporary, i.e. an rvalue. Then as the stardard says, the rvalue reference overload will be selected.

"abc" cannot be passed directly into either overload of fnc(). For both of them it must be converted to an (rvalue) std::string. But then the cited rule from the standard unequivocally selects fnc(std::string&&) over fnc(const std::string&).

Related

Move Constructor choosen instead of Copy. [reference to std]

Can anybody enlighten me where I can find
prvalue:
When used as a function argument and when two overloads of the function are available, one taking rvalue reference parameter and the other taking lvalue reference to const parameter, an rvalue binds to the rvalue reference overload (thus, if both copy and move constructors are available, an rvalue argument invokes the move constructor, and likewise with copy and move assignment operators).
from cppreference
in the standard here
In the final working draft (n4659) of the C++17 standard, it is found in 16.3.3 Best viable function [over.match.best] and specifically in the following subsection:
16.3.3.2 Ranking implicit conversion sequences [over.ics.rank]
3 Two implicit conversion sequences of the same form are
indistinguishable conversion sequences unless one of the following
rules applies:
(3.2) — Standard conversion sequence S1 is a better conversion
sequence than standard conversion sequence S2 if
(3.2.1) — S1 is a
proper subsequence of S2 (comparing the conversion sequences in the
canonical form defined by 16.3.3.1.1, excluding any Lvalue
Transformation; the identity conversion sequence is considered to be a
subsequence of any non-identity conversion sequence) or, if not that,
(3.2.2) — the rank of S1 is better than the rank of S2, or S1 and S2
have the same rank and are distinguishable by the rules in the
paragraph below, or, if not that,
(3.2.3) — S1 and S2 are reference
bindings (11.6.3) and neither refers to an implicit object parameter
of a non-static member function declared without a ref-qualifier, and
S1 binds an rvalue reference to an rvalue and S2 binds an lvalue
reference
I believe (3.2.3) applies to your case.
There are some relevant examples that follow:
int i;
int f1();
int&& f2();
int g(const int&);
int g(const int&&);
int j = g(i); // calls g(const int&)
int k = g(f1()); // calls g(const int&&)
int l = g(f2()); // calls g(const int&&)

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.

Returning by value to rvalue reference

I'm studying rvalue references and I have a doubt in the following code:
string func() {
return "Paul";
}
int main()
{
string&& nodanger = func();
// The lifetime of the temporary is extended
// to the life-time of the reference.
return 0;
}
The question is: what does func() return?
I believe this is what happens:
func returns a prvalue "Paul" (is this a const char * due to a rvalue->pointer conversion?)
a string object is implicitly constructed (which ctor is used?)
due to reference collapsing rules it is bound to "nodanger" (does this behave any differently from a string& normal reference?)
Your func() function returns an std::string prvalue. The constructor being used to construct the std::string is
basic_string( const CharT* s,
const Allocator& alloc = Allocator() );
This prvalue is bound to the rvalue reference nodanger, which extends its lifetime to match that of the reference itself. Reference collapsing doesn't come into play here.
does this behave any differently from a string& normal reference?
The code wouldn't compile if nodanger was a string& because you can't bind rvalues to non-const lvalue references. The lifetime extension behavior in your example is identical to the following case
std::string const& nodanger = func();
Definitely lots of confusion here. I'll assume middle_name() is supposed to be func().
The question is: what does func() return?
It returns a string (which I will assume is a std::string). This return object is initialised with the expression "Paul" which has type "array of 5 const char". The constructor used is the following:
basic_string( const CharT* s,
const Allocator& alloc = Allocator() );
To call this function, the string literal has to undergo implicit array-to-pointer conversion (taking us from a const char[5] to a const char*.
The expression func() is a prvalue expression (a subset of rvalue expressions) because it returns by value. Here's how the standard defines this:
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.
It doesn't make sense to say "returns a prvalue". A "prvalue" is not an object or a type. It's a category of expression. The expression which calls func is a prvalue.
The rvalue reference binds to the std::string object that has been returned from the function. There's no reference collapsing going on here. If the reference were an lvalue reference instead, your code wouldn't compile because a non-const lvalue reference can't bind to an rvalue expression.

C++0x rvalue references and temporaries

(I asked a variation of this question on comp.std.c++ but didn't get an answer.)
Why does the call to f(arg) in this code call the const ref overload of f?
void f(const std::string &); //less efficient
void f(std::string &&); //more efficient
void g(const char * arg)
{
f(arg);
}
My intuition says that the f(string &&) overload should be chosen, because arg needs to be converted to a temporary no matter what, and the temporary matches the rvalue reference better than the lvalue reference.
This is not what happens in GCC and MSVC (edit: Thanks Sumant: it doesn't happen in GCC 4.3-4.5). In at least G++ and MSVC, any lvalue does not bind to an rvalue reference argument, even if there is an intermediate temporary created. Indeed, if the const ref overload isn't present, the compilers diagnose an error. However, writing f(arg + 0) or f(std::string(arg)) does choose the rvalue reference overload as you would expect.
From my reading of the C++0x standard, it seems like the implicit conversion of a const char * to a string should be considered when considering if f(string &&) is viable, just as when passing a const lvalue ref arguments. Section 13.3 (overload resolution) doesn't differentiate between rvalue refs and const references in too many places. Also, it seems that the rule that prevents lvalues from binding to rvalue references (13.3.3.1.4/3) shouldn't apply if there's an intermediate temporary - after all, it's perfectly safe to move from the temporary.
Is this:
Me misreading/misunderstand the standard, where the implemented behavior is the intended behavior, and there's some good reason why my example should behave the way it does?
A mistake that the compiler vendors have somehow all made? Or a mistake based on common implementation strategies? Or a mistake in e.g. GCC (where this lvalue/rvalue reference binding rule was first implemented), that was copied by other vendors?
A defect in the standard, or an unintended consequence, or something that should be clarified?
EDIT: I have a follow-on question that is related: C++0x rvalue references - lvalues-rvalue binding
GCC is doing it wrong according the FCD. The FCD says at 8.5.3 about reference binding
If the reference is an lvalue reference and the initializer expression is an [lvalue / class type] ...
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 and the initializer expression shall be an rvalue or have a function type.
Your case for the call to the std::string && matches none of them, because the initializer is an lvalue. It doesn't get to the place to create a temporary rvalue, because that toplevel bullet already requires an rvalue.
Now, overload resolution doesn't directly use reference binding to see whether there exist an implicit conversion sequence. Instead, it says at 13.3.3.1.4/2
When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 13.3.3.1.
Thus, overload resolution figures out a winner, even though that winner may actually not be able to bind to that argument. For example:
struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };
void f(int&);
void f(B);
int main() { A a; f(a.bits); }
Reference binding at 8.5 forbids bitfields to bind to lvalue references. But overload resolution says that the conversion sequence is the one converting to int, thus succeeding even though when the call is made later, the call is ill-formed. Thus my bitfields example is ill-formed. If it was to choose the B version, it would have succeeded, but needed a user defined conversion.
However, there exist two exceptions for that rule. These are
Except for an implicit object parameter, for which see 13.3.1, a standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const to an rvalue or binding an rvalue reference to an lvalue.
Thus, the following call is valid:
struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };
void f(int&); /* binding an lvalue ref to non-const to rvalue! */
void f(B);
int main() { A a; f(1); }
And thus, your example calls the const T& version
void f(const std::string &);
void f(std::string &&); // would bind to lvalue!
void g(const char * arg) { f(arg); }
However, if you say f(arg + 0), you create an rvalue, and thus the second function is viable.
It was a defect in the standard draft you read. This defect got in as a side effect of some eager editing to disallow binding of rvalue references to lvalues for safety reasons.
Your intuition is right. Of course, there is no harm in allowing an rvalue reference to refer to some unnamed temporary even if the initializer was an lvalue expression. After all, this is what rvalue references are for. The issue you observed has been fixed last year. The upcoming standard will mandate that the second overload will be picked in your example where the rvalue reference will refer to some temporary string object.
The rule fix made it into the draft n3225.pdf (2010-11-27):
[...]
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 and the initializer expression shall be an rvalue or have a function type. [...]
[...]
Otherwise, a temporary of [...] is created [...]
double&& rrd3 = i; // rrd3 refers to temporary with value 2.0
But N3225 seems to have missed to say what i is in this example. The latest draft N3290 contains these examples:
double d2 = 1.0;
double&& rrd2 = d2; // error: copying lvalue of related type
int i3 = 2;
double&& rrd3 = i3; // rrd3 refers to temporary with value 2.0
Since your MSVC version was released before this issue got fixed, it still handles rvalue references according to the old rules. The next MSVC version is expected to implement the new rvalue reference rules (dubbed "rvalue references 2.1" by MSVC developers) see link.
I did not see the behavior mentioned by Doug on g++. g++ 4.5 and 4.4.3 both call f(string &&) as expected but VS2010 calls f(const string &). Which g++ version are you using?
A lot of things in the current draft of the standard need clarification, if you ask me. And the compilers are still developing, so it's hard to trust their help.
It looks pretty clear that your intuition is right… temporaries of any kind are supposed to bind to rvalue references. For example, §3.10, the new "taxonomy" section, categorically defines temporaries as rvalues.
The problem may be that the RR argument specification is insufficient to invoke the creation of a temporary. §5.2.2/5: "Where a parameter is of const reference type a temporary object is introduced if needed." That sounds suspiciously exclusive.
Seems to slip through the cracks again at §13.3.3.1/6: (emphasis mine)
When the parameter type is not a reference, the implicit conversion sequence models a copy-initialization of the parameter from the argument expression. The implicit conversion sequence is the one required to convert the argument expression to a prvalue of the type of the parameter.
Note that copy-initialization string &&rr = "hello"; works fine in GCC.
EDIT: Actually the problem doesn't exist on my version of GCC. I'm still trying to figure out how the second standard conversion of the user-defined conversion sequence relates to forming an rvalue reference. (Is RR formation a conversion at all? Or is it dictated by scattered tidbits like 5.2.2/5?)
Take a look at this:
http://blogs.msdn.com/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx
rvalue references: overload resolution
It looks like your case is: "Lvalues strongly prefer binding to lvalue references".
I don't know if that has changed in the latest versions of the standard, but it used to say something like "if in doubt, don't use the rvalue reference". Probably for compatibility reasons.
If you want the move semantics, use f(std::move(arg)), that works with both compilers.

non-class rvalues always have cv-unqualified types

§3.10 section 9 says "non-class rvalues always have cv-unqualified types". That made me wonder...
int foo()
{
return 5;
}
const int bar()
{
return 5;
}
void pass_int(int&& i)
{
std::cout << "rvalue\n";
}
void pass_int(const int&& i)
{
std::cout << "const rvalue\n";
}
int main()
{
pass_int(foo()); // prints "rvalue"
pass_int(bar()); // prints "const rvalue"
}
According to the standard, there is no such thing as a const rvalue for non-class types, yet bar() prefers to bind to const int&&. Is this a compiler bug?
EDIT: Apparently, this is also a const rvalue :)
EDIT: This issue seems to be fixed in g++ 4.5.0, both lines print "rvalue" now.
The committee already seems to be aware that there's a problem in this part of the standard. CWG issue 690 talks about a somewhat similar problem with exactly the same part of the standard (in the "additional note" from September, 2009). I'd guess new language will be drafted for that part of the standard soon.
Edit: I've just submitted a post on comp.std.c++, noting the problem and suggesting new wording for the relevant piece of the standard. Unfortunately, being a moderated newsgroup, nearly everybody will probably have forgotten this question by the time it makes it through the approval queue there.
Good point. I guess there are two things to look at: 1) as you pointed out the non-class rvalue thingsy and 2) how overload resolution works:
The selection criteria for the best
function are the number of arguments,
how well the arguments match the
parameter-type-list of the candidate
function, [...]
I haven't seen anything in the standard that tells me non-class rvalues are treated specially during overload resolution.
Your question is covered in the draft of the standard I have though (N-4411) somewhat:
What does come into play is however a parallel reading of reference binding, implicit conversion sequences, references, and overload resolution in general:
13.3.3.1.4 Reference binding
2 When a parameter of reference type
is not bound directly to an argument
expression, the conversion sequence
is the one required to convert the argument expression to the underlying
type of the reference according
to 13.3.3.1.
and
13.3.3.2 Ranking implicit conversion sequences
3 Two implicit conversion sequences of
the same form are indistinguishable
conversion sequences unless one of the
following rules applies:
— Standard conversion sequence S1 is a better conversion sequence than
standard
conversion sequence S2 if
— S1 and S2 are reference bindings (8.5.3) and neither refers to an
implicit object parameter of a
nonstatic
member function declared without a ref-qualifier, and either S1 binds an
lvalue reference
to an lvalue and S2 binds an rvalue reference or S1 binds an rvalue
reference to an rvalue and S2
binds an lvalue reference.
[ Example:
int i;
int f();
int g(const int&);
int g(const int&&);
int j = g(i); // calls g(const int&)
int k = g(f()); // calls g(const int&&)