代码之家  ›  专栏  ›  技术社区  ›  onqtam

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

  •  1
  • onqtam  · 技术社区  · 7 年前
    #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?这个左值在什么时候变成右值并被移动?

    5 回复  |  直到 7 年前
        1
  •  5
  •   HolyBlackCat    7 年前

    std::forward 用于通用参考。

    的参数 multicast_func 不是一个普遍的参考,所以 STD::向前 毫无意义:

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

    在这种情况下,它实际上表现为 std::move (因为模板参数不是(lvalue)引用)。

        2
  •  4
  •   SergeyA    7 年前

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

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

    当使用非引用类型调用时,它会有效地从参数中生成右值引用,而不是从参数中移动。 std::vector 在被移走后保证是空的,所以 size 变为0。

        3
  •  1
  •   songyuanyao    7 年前

    这个左值在什么时候变成右值并被移动?

    第一次 proxy 被调用 v 转换为右值,然后在传递到 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));
    }
    

    中的用法 代理 是的一般用法 std::forward ;根据参数是左值或右值,模板参数将推导为 T& T .用于 T& STD::向前 将返回左值,用于 T STD::向前 将返回右值,因此保留值类别。如果仅指定模板参数,则会丢失此类容量。

        4
  •  1
  •   NathanOliver    7 年前

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

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

    将使 v 一个右值,意味着它将移动到 real_func .然后,第二个调用使用从对象移动的对象,您将获得 0 因为它是空的。

    如果我们看函数的话,这是有意义的。版本 STD::向前 你打电话是

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

    自从你过世后 std::vector<int> 对于 T 这意味着它将返回 std::vector<int>&& .所以,尽管如此 是左值,它被转换为右值。如果你想保持 然后你需要使用 std::vector<int>& .这给了你 std::vector<int>& && 引用colapse规则将其转化为 STD::向量& LT. INT> 给你一个左值。

        5
  •  0
  •   rsy56640    7 年前

    第一次呼叫 proxy ,参数 vector<int> v (功能上 real_func )是从右值构造的,所以 v (功能上 multicast_func )已为空。
    但是如果你把pareter的类型改成 cosnt vector<int> & ,结果是3,3。因为尚未调用移动构造函数。

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