【问题标题】:Universal reference vs non-template `const&` [duplicate]通用参考与非模板`const&` [重复]
【发布时间】:2020-08-12 18:08:07
【问题描述】:

考虑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);
}

上面的代码打印“2”。为什么第二个重载被认为是更好的匹配?
第一个归结为foo(const int&amp;, const double&amp;),它是一个常规函数,所以应该首选它,不是吗?

我猜这不是“完全匹配”,但这里到底是什么不“完全”?

【问题讨论】:

  • 你用的是什么编译器? gcc 和 clang print 1.
  • 为我打印1 godbolt.org/z/P69d7q
  • Us&amp; - 这不是转发参考
  • 请注意,链接中的代码与问题中的代码不匹配。问题中的函数接受Us&amp;...,而链接中的函数接受Us&amp;&amp;...。
  • @cigien 看起来更像是 OP 只是在问题中键入了代码,并且很困惑为什么通用引用重载优于 const 引用重载。

标签: c++ language-lawyer overload-resolution


【解决方案1】:

首选第二个foo 的特化,因为指定模板参数推导和重载解析的方式。

仅对第二个foo 进行模板参数推导。

Us&amp;&amp; 是一个转发参考 [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&amp;&amp;,double&amp;&amp;)

第一个重载类型为void (const int &amp;, const double &amp;)。

在模板参数推导之后,编译器必须选择哪一个是最可行的。规则列表很长,但不同之处在于,物化临时对象到引用参数的绑定的隐式转换序列对于第二个重载的两个参数绑定比第一个重载[over.ics.rank]§3.2.3 的绑定更好:

S1 和 S2 包括引用绑定 ([dcl.init.ref]) 并且都不引用在没有 ref 限定符的情况下声明的非静态成员函数的隐式对象参数,并且 S1 将右值引用绑定到右值和S2 绑定一个左值引用

【讨论】:

  • 这里好像3.2.3适用(而不是3.2.6),不是吗?
  • @IgorR。确实很明显......你可以单独修复它!
【解决方案2】:

精确匹配实际上是 int&&,这就是选择第二个重载的原因。
原因是文字是临时的,可以直接用作右值。
在用于 const int & 的情况下,字面量通过调用移动构造函数 (int(int &amp;&amp;)) 来初始化另一个用于引用的临时值,类似于:

const int i = 1;
const double d = 1.0;
f(i, d);

这将调用第一个重载。

【讨论】:

  • 文字是临时的不。文字通过调用移动构造函数(int(int &&)) 什么???
  • @LanguageLaywer:这是将临时绑定到 const 引用的简化。
  • @LanguageLawyer 你能详细说明一下文字不是临时的吗?
  • @HolyBlackCat int 或 double 文字是一个纯右值,其值是由文字“编码”的,没有对象持有该值。
  • @HolyBlackCat int 或 double (等等)prvalues 甚至在 C++17 之前就没有将它们的值存储在对象中。例如。在 C++14 中:An rvalue ... is an xvalue, a temporary object or subobject thereof, or a value that is not associated with an object.(«An rvalue .. is a temporary object ...» - 感谢 RSmith,此 BS 已被删除。我的意思是,右值是表达式而不是对象。)
猜你喜欢
  • 2020-07-13
  • 1970-01-01
  • 2020-12-13
  • 1970-01-01
  • 2012-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多