使用内存移动构造函数

Move constructor with memcpy

本文关键字:构造函数 移动 内存      更新时间:2023-10-16

>我有一个结构,我想它是不可复制的,只能移动的,但由于它包含很多 POD,编写移动构造函数会很长,忘记变量很难调试。例:

struct myStruct{
    int a,b,c,d;
    double e,f,g,h;
    std::complex<double> value1,value2;
    std::unique_ptr<Calculator> calc;
    myStruct(){}
    myStruct(const myStruct &)=delete;
    myStruct(myStruct && other);
};

这种移动构造函数会有什么问题:

myStruct::myStruct(myStruct && other){
    std::memcpy(this,&other,sizeof(myStruct));
    other.calc.release();
    calc->rebind(this);
}

我可能面临什么问题,这是否定义明确?

最小的更改只是将简单初始化的成员组合在一起,以便您可以轻松地memcpy它们:

struct myStruct{
    struct {
        int a,b,c,d;
        double e,f,g,h;
        std::complex<double> value1,value2;
    } pod;
    std::unique_ptr<Calculator> calc;
    myStruct(){}
    myStruct(const myStruct &)=delete;
    myStruct(myStruct && other);
};
myStruct::myStruct(myStruct && other){
    std::memcpy(&pod,&other.pod,sizeof(pod));
    other.calc.release();
    calc->rebind(this);
}

注意 std::complex 是一种文本类型,放入 pod 成员中应该是安全的。如果添加任何其他类类型的成员对象,则必须验证自己是否对memcpy安全。


正如 Jonathan Wakely 指出的那样,更好的实现将回避对 pod 和非 pod(或文字和简单初始化)成员的担忧。相反,按是否要复制或移动成员对成员进行分组:

struct myStruct{
    struct {
        int a,b,c,d;
        double e,f,g,h;
        std::complex<double> value1,value2;
    } val;
    std::unique_ptr<Calculator> calc;
    myStruct(){}
    myStruct(const myStruct &)=delete;
    myStruct(myStruct && other);
};
myStruct::myStruct(myStruct && other)
  : val(other.val)              // copy the value types
  , calc(std::move(other.calc)) // and move the reference types
{
    calc->rebind(this);
}

您可以使用默认的移动 Ctor:

myStruct(myStruct&& other) = default;

编辑:在最近的编辑之后,我更喜欢@Useless接受的答案。下面演示一种替代方法,该方法允许将移动构造函数定义为默认值,但与接受的答案相比,该方法被过度设计。

使用memcpy是一个坏主意。我可能会考虑创建一个执行非三重工作(移动unique_ptr并重新绑定计算器)的帮助程序类型,并在 myStruct 中使用它,以便可以默认其移动操作:

struct RebindableCalc
{
  RebindableCalc();
  RebindableCalc(RebindableCalc&& r) noexcept : calc(std::move(r.calc))
  { calc->rebind(self()); }
  RebindableCalc& operator=(RebindableCalc&& r) noexcept
  {
    calc = std::move(r.calc);
    calc->rebind(self());
    return *this;
  }
  std::unique_ptr<Calculator> calc;
  myStruct* self();
};
struct myStruct : RebindableCalc
{
    int a,b,c,d;
    double e,f,g,h;
    std::complex<double> value1,value2;
    myStruct() = default;
    myStruct(myStruct&& other) = default;
    myStruct& operator=(myStruct&& other) = default;
};
inline myStruct* RebindableCalc::self()
{
  return static_cast<myStruct*>(this);
}

这将允许编译器为 POD 成员生成最佳代码,并且仍然为 unique_ptr<Calculator> 成员执行正确的操作。无需memcpy。 如果您向myStruct添加更多成员,移动操作仍将执行正确的操作。