首页 > 解决方案 > 为什么这个重载决议选择带有右值引用的签名?

问题描述

以下程序使用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);
}

https://godbolt.org/z/jdbEd1

AFAICSshmoo->bar应该是对shmoo对象的 bar 字段的左值引用。我在这里看不到“转换序列”来从中进行右值引用,但我承认这里发生了很多我不明白的事情。

标签: c++rvalue-reference

解决方案


在您的示例代码中,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&` 相比,这是一个精确匹配。

结果,调用了前向引用重载。


推荐阅读