问题描述
以下程序将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,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
将成为其输入的确切类型,与CV限定词和值类别都匹配。
- 对于类型为
T
的PR值,这将产生U = T
- 对于类型为
T
的X值,将产生U = T&&
- 对于类型为
T
的L值,这将产生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
将const
的非std::vector<int>
左值引用传递到append_list
中。
在重载解析期间,编译器将始终以最匹配的方式解析该函数(即,所需的转换次数最少)。在上述重载中,std::vector<int>&
可以匹配到const U&
= const std::vector<T>&
,但是与匹配{{1 }} std :: vector&`完全匹配。
因此,将调用前向引用重载。