Function overloads priority (references)

296 views Asked by At
void func(const int &) { std::cout << "c lv ref\n"; }
void func(int &&) { std::cout << "rv ref\n"; }

func(1);

Since const lvalue reference is able to accept every kind of data (const and non- lval, const and non- rvalue), I wonder what is the guarantee that the code above will print "rv ref". Is it standardized or compiler dependent?

1

There are 1 answers

4
Vittorio Romeo On BEST ANSWER

My previous explanation was incorrect - as T.C. mentioned in the comments, both overloads have the same implicit conversion sequence, and the disambiguation happens as a special tie-breaker defined in the standard.


§13.3.3.1.4 [over.ics.ref], par. 1

When a parameter of reference type binds directly ([dcl.init.ref]) to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion ([over.best.ics]). [...]

If the argument types are the same, the implicit conversion sequence applied to both overloads is the identity conversion. This does not yet disambiguate the two functions.

The disambiguation is specified as a tie-breaker here:

§13.3.3.2 [over.ics.rank], par 3.2.3

[Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if...] S1 and S2 are reference binding ([dcl.init.ref]) 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. [...]

The quotes imply that, in the case of two identity implicit conversion sequences:

  • The sequence binding an rvalue to rvalue reference is the best one.

  • The sequence binding an rvalue to an lvalue reference is worse than the aforementioned one.


[...] if S1 and S2 are reference binding [...]

The reason why const is required to bind rvalues to const& can be found here:

§8.6.3 [dcl.init.ref], par 5.2

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

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.

If the initializer expression [...] is an rvalue (but not a bit-field) or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”