通过值和移动,或两种方法

Den*_* YL 5 c++ move-semantics perfect-forwarding c++11

假设我有以下类,它有一个方法set_value.哪种实施更好?

class S {
public:
  // a set_value method

private:
  Some_type value;
};
Run Code Online (Sandbox Code Playgroud)

通过价值,然后移动

void S::set_value(Some_type value)
{
  this->value = std::move(value);
}
Run Code Online (Sandbox Code Playgroud)

定义两个重载方法

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

void S::set_value(Some_type&& value)
{
  this->value = std::move(value);
}
Run Code Online (Sandbox Code Playgroud)

第一种方法只需要定义一种方法,而第二种方法需要两种方法.

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

  1. 根据传递的参数复制/移动参数的构造函数
  2. 移动作业
  3. 参数的析构函数

而对于第二种方法,仅执行一次分配操作.

  1. 复制/移动分配取决于调用哪个重载方法

那么,哪种实施更好?或者它是否重要?

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

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

Ric*_*ges 0

编译器可以自由地删除(优化掉)副本,即使这样做会产生副作用。因此,按值传递和移动结果实际上可以为您提供两种方法解决方案的所有性能优势,同时只为您提供一个需要维护的代码路径。您绝对应该更喜欢按值传递。

这是一个例子来证明这一点:

#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;
}
Run Code Online (Sandbox Code Playgroud)

预期输出:

** 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
Run Code Online (Sandbox Code Playgroud)

请注意,复制/移动版本中没有任何冗余副本,但使用set_value_by_const_ref()无名临时调用时存在冗余复制分配。我注意到最终案例的明显效率增益。我认为(a)这是现实中的一个极端情况,(b)优化器可以处理它。

我的命令行:

c++ -o move -std=c++1y -stdlib=libc++ -O3 move.cpp
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

562 次

最近记录:

10 年,5 月 前