c++ - 为什么这个重载决议选择带有右值引用的签名?
问题描述
以下程序使用append_list
具有右值引用签名的函数,而不是 const 引用。为什么?
#include <stdio.h>
#include <iterator>
#include <memory>
#include <vector>
class foo {
public:
std::vector<int> bar{1};
};
template<typename T, typename U>
static void append_list(T &t, const U &u) {
t.insert(t.end(), u.begin(), u.end());
}
template<typename T, typename U>
static void append_list(T &t, U &&u) {
printf("move\n");
std::move(u.begin(), u.end(), std::back_inserter(t));
}
int main() {
auto shmoo = std::make_shared<foo>();
std::vector<int> baz{2};
append_list(baz, shmoo->bar);
}
AFAICSshmoo->bar
应该是对shmoo
对象的 bar 字段的左值引用。我在这里看不到“转换序列”来从中进行右值引用,但我承认这里发生了很多我不明白的事情。
解决方案
在您的示例代码中,U
是转发引用而不是 RValue 引用:
template<typename T, typename U>
static void append_list(T &t, U &&u) {
// ^~~~~
转发引用的行为与通常的模板推导类型不同,因为U
它将成为其输入的确切类型,匹配 CV 限定符和值类别。
- 对于类型的 PR 值
T
,这会产生U = T
- 对于 X 类型的值
T
,这会产生U = T&&
- 对于 类型的 L 值
T
,这会产生U = T&
这与从推导类型const U&
确定的普通模板匹配不同U = T
。
当作为具有通过模板匹配推断其参数的函数模板的重载集呈现时,转发引用将实际上是“贪婪的”——因为在大多数情况下,它将是重载决议的明确更好的匹配。
使用您的示例代码:
int main() {
auto shmoo = std::make_shared<foo>();
std::vector<int> baz{2};
append_list(baz, shmoo->bar);
}
schmoo->bar
正在传递into的非const
左值引用。std::vector<int>
append_list
在重载解析期间,编译器将始终以最精确的匹配解析函数(即需要最少的转换次数)。在上面的重载中,std::vector<int>&
可以匹配到const U&
= const std::vector<T>&
-- 但这需要添加const
, 与匹配的U&& =
std::vector&` 相比,这是一个精确匹配。
结果,调用了前向引用重载。
推荐阅读
- xamarin.forms - Microsoft Azure 存储数据移动库以及 xamarin 表单
- ios - 回到 UIViewController 不会触发 segue
- c# - 我可以帮助 C# 编译器推断这种类型吗?
- c - 如何在 CLion 中设置断点条件
- javascript - 更改 js 函数参数的引用
- sql - 如何不重复使用 SQL where 子句
- android - 如何将图像添加到 ProgressDialog?
- javascript - 使用 Puppeteer 将结果抓取到 JSON 时无限重启
- sql - Cordova 使用事务将大文件导入数据库
- python-3.x - Selenium 返回错误数量的元素