为什么这个重载解析 select 带有右值引用的签名?
Why does this overload resolution select the signature with the rvalue reference?
以下程序使用带有右值引用签名的 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);
}
AFAICS shmoo->bar
应该是对 shmoo
对象的 bar 字段的左值引用。我在这里没有看到一个“转换序列”来制作右值引用,但我承认这里有很多我不明白的地方。
在您的示例代码中,U
是转发引用而不是 RValue 引用:
template<typename T, typename U>
static void append_list(T &t, U &&u) {
// ^~~~~
转发引用的行为不同于通常的模板推导类型,因为 U
将成为其输入的 exact 类型,匹配 CV-qualifiers 和值类别。
- 对于
T
类型的 PR 值,这会产生 U = T
- 对于
T
类型的 X-values,这会产生 U = T&&
- 对于
T
类型的 L-values,这会产生 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
将 std::vector<int>
的非 const
左值引用传递给 append_list
.
在重载解析期间,编译器将始终解析具有最精确匹配的函数(即需要最少数量的转换)。在上面的重载中,std::vector<int>&
可以匹配到const U&
= const std::vector<T>&
——但这需要添加const
,相比于匹配U&& =
std::vector&` 完全匹配。
因此,forward-reference 重载被调用。
以下程序使用带有右值引用签名的 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);
}
AFAICS shmoo->bar
应该是对 shmoo
对象的 bar 字段的左值引用。我在这里没有看到一个“转换序列”来制作右值引用,但我承认这里有很多我不明白的地方。
在您的示例代码中,U
是转发引用而不是 RValue 引用:
template<typename T, typename U>
static void append_list(T &t, U &&u) {
// ^~~~~
转发引用的行为不同于通常的模板推导类型,因为 U
将成为其输入的 exact 类型,匹配 CV-qualifiers 和值类别。
- 对于
T
类型的 PR 值,这会产生U = T
- 对于
T
类型的 X-values,这会产生U = T&&
- 对于
T
类型的 L-values,这会产生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
将 std::vector<int>
的非 const
左值引用传递给 append_list
.
在重载解析期间,编译器将始终解析具有最精确匹配的函数(即需要最少数量的转换)。在上面的重载中,std::vector<int>&
可以匹配到const U&
= const std::vector<T>&
——但这需要添加const
,相比于匹配U&& =
std::vector&` 完全匹配。
因此,forward-reference 重载被调用。