通过值和移动,或两种方法 [英] Pass by value and move, or two methods

查看:133
本文介绍了通过值和移动,或两种方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有下面的类,它有一个方法 set_value 。哪个实施更好?

  class S {
public:
// set_value method

private:
Some_type value;
};



按值传递,然后移动



  void S :: set_value(Some_type value)
{
this-> value = std :: move(value);
}



定义两个重载方法



  void S :: set_value(const Some_type& value)
{
this-> value = value;
}

void S :: set_value(Some_type& value)
{
this-> value = std :: move(value);
}

第一种方法需要定义一个方法, / p>

但是,第一种方法似乎效率较低:


  1. 复制/移动
  2. 移除赋值
  3. 参数的析构函数

对于第二种方法,只执行一个赋值操作。




  1. 那么,哪个实现更好呢?



    还有一个问题:下面的代码是否等同于第二种方法中的两个重载方法?

     模板< class T> 
    void S :: set_value(T&& value)
    {
    this-> value = std :: forward< T&
    }


    解决方案

    (优化远离)副本,即使这样做会有副作用。因此,传递值和移动结果实际上提供了双方法解决方案的所有性能优势,同时仅提供一个代码路径来维护。



    这里有一个例子来证明这一点:

     

    code> #include< iostream>

    struct XYZ {
    XYZ(){std :: cout< 构造< std :: endl; }

    XYZ(const XYZ&){
    std :: cout<< 拷贝构建<< std :: endl;
    }
    XYZ(XYZ&&)noexcept {
    try {
    std :: cout< 移动构建< std :: endl;
    }
    catch(...){

    }
    }

    XYZ& operator =(const XYZ&){
    std :: cout< assigned<< std :: endl;
    return * this;
    }

    XYZ& operator =(XYZ&&){
    std :: cout< move-assigned<< std :: endl;
    return * this;
    }

    };

    struct holder {
    holder(XYZ xyz):_xyz(std :: move(xyz)){}

    void set_value(XYZ xyz){_xyz = std :: move(xyz); }
    void set_value_by_const_ref(const XYZ& xyz){_xyz = xyz; }

    XYZ _xyz;
    };
    using namespace std;

    auto main() - > int
    {
    cout<< **创建命名源以供以后使用**< endl
    XYZ xyz2 {};

    cout<< \\\
    ** initial construction **<< std :: endl;
    holder h {XYZ()};

    cout<< \\\
    ** set_value()**<< endl
    h.set_value(XYZ());

    cout<< \\\
    ** set_value_by_const_ref()with nameless temporary **< endl
    h.set_value_by_const_ref(XYZ());

    cout<< \\\
    ** set_value()with named source **<< endl
    h.set_value(xyz2);

    cout<< \\\
    ** set_value_by_const_ref()with named source **< endl
    h.set_value_by_const_ref(xyz2);
    return 0;
    }

    预期输出:

      **创建命名源供以后使用** 
    构造

    **初始构造**
    构造
    move构造

    ** set_value()**
    构造
    移动分配

    ** set_value_by_const_ref()与无名称临时**
    构造
    分配

    ** set_value()与命名源**
    构造的副本
    移动分配

    ** set_value_by_const_ref()与命名源**
    分配

    注意副本/移动版本,但在调用 set_value_by_const_ref()时使用无名临时的冗余副本分配。我注意到最终案例的表观效率增益。



    我的命令行:

      c ++ -o move -std = c ++ 1y -stdlib = libc ++ -O3 move.cpp 
    / pre>

    Assume I have the following class, which has a method set_value. Which implementation is better?

    class S {
    public:
      // a set_value method
    
    private:
      Some_type value;
    };
    

    Pass by value, then move

    void S::set_value(Some_type value)
    {
      this->value = std::move(value);
    }
    

    Define two overloaded methods

    void S::set_value(const Some_type& value)
    {
      this->value = value;
    }
    
    void S::set_value(Some_type&& value)
    {
      this->value = std::move(value);
    }
    

    The first approach requires definition of one method only while the second requires two.

    However, the first approach seems to be less efficient:

    1. Copy/Move constructor for the parameter depending on the argument passed
    2. Move assignment
    3. Destructor for the parameter

    While for the second approach, only one assignment operation is performed.

    1. Copy/Move assignment depending on which overloaded method is called

    So, which implementation is better? Or does it matter at all?

    And one more question: Is the following code equivalent to the two overloaded methods in the second approach?

    template <class T>
    void S::set_value(T&& value)
    {
      this->value = std::forward<T>(value);
    }
    

    解决方案

    The compiler is free to elide (optimise away) the copy even if there would be side effects in doing so. As a result, passing by value and moving the result actually gives you all of the performance benefits of the two-method solution while giving you only one code path to maintain. You should absolutely prefer to pass by value.

    here's an example to prove it:

    #include <iostream>
    
    struct XYZ {
        XYZ() { std::cout << "constructed" << std::endl; }
    
        XYZ(const XYZ&) {
            std::cout << "copy constructed" << std::endl;
        }
        XYZ(XYZ&&) noexcept {
            try {
                std::cout << "move constructed" << std::endl;
            }
            catch(...) {
    
            }
        }
    
        XYZ& operator=(const XYZ&) {
            std::cout << "assigned" << std::endl;
            return *this;
        }
    
        XYZ& operator=(XYZ&&) {
            std::cout << "move-assigned" << std::endl;
            return *this;
        }
    
    };
    
    struct holder {
        holder(XYZ xyz) : _xyz(std::move(xyz)) {}
    
        void set_value(XYZ xyz) { _xyz = std::move(xyz); }
        void set_value_by_const_ref(const XYZ& xyz) { _xyz = xyz; }
    
        XYZ _xyz;
    };
    using namespace std;
    
    auto main() -> int
    {
        cout << "** create named source for later use **" << endl;
        XYZ xyz2{};
    
        cout << "\n**initial construction**" << std::endl;
        holder h { XYZ() };
    
        cout << "\n**set_value()**" << endl;
        h.set_value(XYZ());
    
        cout << "\n**set_value_by_const_ref() with nameless temporary**" << endl;
        h.set_value_by_const_ref(XYZ());
    
        cout << "\n**set_value() with named source**" << endl;
        h.set_value(xyz2);
    
        cout << "\n**set_value_by_const_ref() with named source**" << endl;
        h.set_value_by_const_ref(xyz2);
        return 0;
    }
    

    expected output:

    ** create named source for later use **
    constructed
    
    **initial construction**
    constructed
    move constructed
    
    **set_value()**
    constructed
    move-assigned
    
    **set_value_by_const_ref() with nameless temporary**
    constructed
    assigned
    
    **set_value() with named source**
    copy constructed
    move-assigned
    
    **set_value_by_const_ref() with named source**
    assigned
    

    note the absence of any redundant copies in the copy/move versions but the redundant copy-assignment when calling set_value_by_const_ref() with nameless temporary. I note the apparent efficiency gain of the final case. I would argue that (a) it's a corner case in reality and (b) the optimiser can take care of it.

    my command line:

    c++ -o move -std=c++1y -stdlib=libc++ -O3 move.cpp
    

    这篇关于通过值和移动,或两种方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆