#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);
}
上面的代码打印“ 2”。为什么第二个过载被认为是更好的匹配?
第一个归结为foo(const int&, const double&)
,它是一个常规函数,因此应该首选,不是吗?
我猜这不是“完全匹配”,但是这里到底有什么不是“完全匹配”?
答案 0 :(得分:1)
首选第二个foo
的专业化,因为指定模板参数推导和重载解析的方式。
仅对第二个foo
进行模板参数推导。
Us&&
是转发参考 [temp.deduct.call]§3
如果P是转发引用,且参数是左值,则使用类型“对A的左值引用”代替A进行类型推导。
其中P
是函数参数without cv qualifier and without reference的类型。 A
是参数的类型。参数是一个表达式,表达式从不具有引用类型。第一个参数是类型int
的 prvalue ,第二个参数是类型double
的 prvalue 。 prvalue 不是左值,因此推论P
为第一个参数的int
和第二个参数为double
。
因此,在扣除模板参数后,第二个重载类型为void (int&&,double&&)
第一个重载的类型为void (const int &, const double &)
。
在推导模板参数之后,编译器必须选择哪种重载是最可行的。规则列表很长,但不同之处在于,第二个重载的两个参数绑定比第一个重载的{{3 }}:
S1和S2包括引用绑定([dcl.init.ref]),并且都没有引用没有引用限定符声明的非静态成员函数的隐式对象参数,并且S1将右值引用绑定到右值和S2绑定左值引用
答案 1 :(得分:-1)
确切的匹配实际上是int &&,这就是第二次重载被选中的原因。
原因是文字是临时的,可以直接用作右值。
如果将其用于const int&,则字面量会通过调用move构造函数(int(int &&)
)来初始化另一个用于引用的临时文件,类似于:
const int i = 1;
const double d = 1.0;
f(i, d);
哪个会称为第一个重载。