Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Universal reference vs non-template `const&` [duplicate]

Consider the following code:

#include <iostream>
#include <type_traits>

template <class... Ts>
struct test {
    static void foo(const std::remove_reference_t<Ts>&...) {
        std::cout << "1\n";
    }

    template <class... Us>
    static void foo(Us&&...) {
        std::cout << "2\n";
    }
};

int main() {
    test<int&, double&>::foo(1, 1.0);
}

The above code prints "2". Why is the second overload considered a better match?
The first one boils down to foo(const int&, const double&) and it's a regular function, so it should be preferred, shouldn't it?

I guess it's not an "exact match", but what is not "exact" here exactly?

like image 806
Igor R. Avatar asked Jun 06 '26 23:06

Igor R.


1 Answers

A specialization of the second foo is prefered because of the way is specified template argument deduction and overload resolution.

Template argument deduction takes place only for the second foo.

Us&& is a forwarding reference [temp.deduct.call]§3

if P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.

where P is the type of the function parameter without cv qualifier and without reference. A is the type of the argument. An argument is an expression and expressions never have reference type. The first argument is a prvalue of type int and the second a prvalue of type double. A prvalue is not an lvalue so P is deduced to be int for the first argument and double for the second argument.

So after template argument deduction the second overload type is void (int&&,double&&)

The first overload has type void (const int &, const double &).

After template argument deduction, the compiler must choose which of the too overloads is the best viable one. The list of rules is long but the difference happen to be that the implicit conversion sequences of the bindings of the materialized temporaries to the the reference parameters is better for the two argument bindings of the second overload than for those of the first overload [over.ics.rank]§3.2.3:

S1 and S2 include reference bindings ([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

like image 74
Oliv Avatar answered Jun 08 '26 13:06

Oliv



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!