#include <iostream>
using namespace std;
void func(int (&ref)[6]) { cout << "#1" << endl; }
void func(int * &&ref) { cout << "#2" << endl; }
int main()
{
int arr[6];
func(arr); // g++(5.4): ambiguous, clang++(3.8): #2, vc++(19.11): #1
return 0;
}
Both functions are exact matches. Below is a quote from the standard:
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.
Doesn't it imply that the second is better?
Updated:
There is a related question. And the following code is a simplified version of it.
#include <iostream>
using namespace std;
void func(int *&) { cout << "#1" << endl; }
void func(int *&&) { cout << "#2" << endl; }
int main()
{
int arr[6];
func(arr); // g++(5.4) and clang++(3.8): #2, vc++(19.11): ambiguous
return 0;
}
“l-value” refers to a memory location that identifies an object. “r-value” refers to the data value that is stored at some address in memory. References in C++ are nothing but the alternative to the already existing variable.
An lvalue reference can bind to an lvalue, but not to an rvalue.
Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.
To answer the titular question, "rvalue reference" is a kind of type, while "xvalue" is a kind of expression. They are not. Rvalue references are types, types are not expressions and so cannot be "considered lvalue".
You can overload a function to take an lvalue reference and an rvalue reference. By overloading a function to take a const lvalue reference or an rvalue reference, you can write code that distinguishes between non-modifiable objects (lvalues) and modifiable temporary values (rvalues).
The function f takes an rvalue reference as its parameter (a named rvalue reference) and returns an rvalue reference (an unnamed rvalue reference). In the call to g from f, overload resolution selects the version of g that takes an lvalue reference because the body of f treats its parameter as an lvalue.
The reference declared in the above code is lvalue reference (i.e., referring to variable in the lvalue) similarly the references for the values can also be declared. rvalue references have two properties that are useful: rvalue references extend the lifespan of the temporary object to which they are assigned.
Before overload resolution begins, the functions selected by name lookup and template argument deduction are combined to form the set of candidate functions (the exact criteria depend on the context in which overload resolution takes place, see below).
I think it depends on what a particular phrase means.
Both conversions are equivalent because we exclude lvalue transformations (basically, an array effectively is a pointer so it doesn't count as a conversion), so we get into the next tiebreaker that you pointed out in [over.ics.rank]:
S1 and S2 are reference bindings 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
Does this case apply? We do have two reference bindings:
int arr[6];
int (&a)[6] = arr; // #1
int *&& b = arr; // #2
Here, #1 binds an lvalue reference. #2 falls into [dcl.init.ref]:
Otherwise, the initializer expression is implicitly converted to a prvalue of type “cv1 T1”. The temporary materialization conversion is applied and the reference is bound to the result.
arr
is implicitly converted to a prvalue of type int*
, which is then bound to b
.
So now the question is - what does the restriction in [over.ics.rank] mean? It could mean:
arr
. arr
is not an rvalue (it is an lvalue), this tiebreaker is skipped and no subsequent tiebreakers apply.I am inclined to favor gcc's implementation here. Otherwise, what would the point of the phrase "binds an rvalue reference to an rvalue" be? Rvalue references cannot bind to lvalues. It's redundant. That said, it's awkwardly worded for that interpretation too.
As is, I'll call it a wording bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With