不理解左值的双重转发 - 按值传递时

not understanding double forwarding of an lvalue - when passed by value

#include <iostream>
#include <vector>
#include <type_traits>
#include <utility>

using namespace std;

template <typename Func, typename... Args>
void proxy(Func f, Args&&... args) {
    f(std::forward<Args>(args)...);
}

void real_func(vector<int> v) {
    cout << "size: " << v.size() << endl;
}

void multicast_func(vector<int> v) {
    proxy(real_func, std::forward<vector<int>>(v));
    proxy(real_func, std::forward<vector<int>>(v));
}

int main()
{
    vector<int> ints = {1, 2, 3};
    multicast_func(ints);
    return 0;
}

输出为:

size: 3
size: 0

为什么不是 3、3?这个左值在什么时候变成右值并被移出?

at what point does this lvalue become an rvalue and gets moved-from?

在第一次调用 proxyv 被转换为右值,然后在传递给 real_func 时被移出。

void multicast_func(vector<int> v) {

    // the return type of std::forward is T&&, i.e. vector<int>&& here
    // for functions whose return type is rvalue reference to objec, the return value is an rvalue
    // that means v is converted to an rvalue and passed to proxy
    proxy(real_func, std::forward<vector<int>>(v));

    // v has been moved when passed to real_func as argument
    proxy(real_func, std::forward<vector<int>>(v));
}

proxy中的用法是std::forward的一般用法;根据参数是左值还是右值,模板参数将推导为T&T。对于 T& std::forward 将 return 一个左值,对于 T std::forward 将 return 一个右值,因此值类别被保留。当您单独指定模板参数时,这样的容量就会丢失。

std::forward 旨在与通用引用一起使用。

multicast_func的参数不是通用引用,所以std::forward没有意义:

void multicast_func(vector<int> v) {
    proxy(real_func, std::forward<vector<int>>(v));
    proxy(real_func, std::forward<vector<int>>(v));
}

在这种情况下,它的作用类似于std::move(因为模板参数不是(左值)引用)。

std::forward,当没有给定引用类型时,会将提供的对象转换为右值。这意味着第一次调用

proxy(real_func, std::forward<vector<int>>(v));

将使 v 成为右值,这意味着它会将其移至 real_func。然后第二次调用使用从对象移动的那个,你得到 0 的大小,因为它已被清空。

如果我们看一下函数,这是有道理的。您调用的 std::forward 的版本是

template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;

由于您通过 std::vector<int> 获得 T,这意味着它将 return 成为 std::vector<int>&&。因此,即使 v 是左值,它也会被转换为右值。如果你想保持 v 的左值性,那么你需要使用 std::vector<int>&。这给了你 std::vector<int>& && 并且引用折叠规则将它变成了 std::vector<int>& 给你留下了一个左值。

您的代码中调用的 std::forward 的原型是:

template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;

当使用非引用类型调用时,它有效地从参数中创建右值引用,然后从中移动。 std::vector被移出后保证为空,所以size变为0。

proxy的第一次调用中,参数vector<int> v(在函数real_func中)是从右值构造的,所以v(在函数multicast_func) 已为空。
但是如果你把参数类型改成cosnt vector<int> &,结果就是3, 3。因为还没有调用move constrctor。

void real_func(const vector<int>& v) {
    cout << "size: " << v.size() << endl;// the output is 3, 3
}