C++ 使用memcpy移动构造函数

C++ 使用memcpy移动构造函数,c++,c++11,move-constructor,C++,C++11,Move Constructor,我有一个结构,我想是不可复制的,只能移动的,但因为它包含很多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 myStru

我有一个结构,我想是不可复制的,只能移动的,但因为它包含很多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&& other) = default;

您可以使用默认的移动选择器:

myStruct(myStruct&& other) = default;

最小的更改只是将初始化的成员分组在一起,这样您就可以轻松地
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);
}

最小的更改只是将初始化的成员分组在一起,这样您就可以轻松地
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);
}

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

使用
memcpy
是个坏主意。我可能会考虑创建一个辅助类型来执行非琐碎的工作(移动<代码> UNQUIGYPTR 并重新绑定计算器)并在MyStult中使用它,这样它的移动操作就可以默认:

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);
}
struct RebindableCalc
{
RebindableCalc();
重新绑定计算(重新绑定计算和&r)无例外:计算(标准::移动(r.calc))
{calc->rebind(self());}
RebindableCalc&operator=(RebindableCalc&r)无例外
{
计算=标准::移动(r.calc);
计算->重新绑定(self());
归还*这个;
}
std::唯一计算;
myStruct*self();
};
结构myStruct:RebindableCalc
{
INTA、b、c、d;
双e,f,g,h;
标准::复杂值1,值2;
myStruct()=默认值;
myStruct(myStruct&&other)=默认值;
myStruct&operator=(myStruct&other)=默认值;
};
内联myStruct*RebindableCalc::self()
{
返回静态_cast(此);
}

这将使编译器为POD成员生成最佳代码,并且仍然为
unique_ptr
成员执行正确的操作。不需要
memcpy
。如果您向myStruct添加更多成员,移动操作仍然会做正确的事情。

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

使用
memcpy
是个坏主意。我可能会考虑创建一个辅助类型来执行非琐碎的工作(移动<代码> UNQUIGYPTR 并重新绑定计算器)并在MyStult中使用它,这样它的移动操作就可以默认:

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);
}
struct RebindableCalc
{
RebindableCalc();
重新绑定计算(重新绑定计算和&r)无例外:计算(标准::移动(r.calc))
{calc->rebind(self());}
RebindableCalc&operator=(RebindableCalc&r)无例外
{
计算=标准::移动(r.calc);
计算->重新绑定(self());
归还*这个;
}
std::唯一计算;
myStruct*self();
};
结构myStruct:RebindableCalc
{
INTA、b、c、d;
双e,f,g,h;
标准::复杂值1,值2;
myStruct()=默认值;
myStruct(myStruct&&other)=默认值;
myStruct&operator=(myStruct&other)=默认值;
};
内联myStruct*RebindableCalc::self()
{
返回静态_cast(此);
}


这将使编译器为POD成员生成最佳代码,并且仍然为
unique_ptr
成员执行正确的操作。不需要
memcpy
。如果您向
myStruct
添加更多成员,移动操作仍然会做正确的事情。

unique\u ptr
肯定会出乱子。它可能必须引用单个内存对象,因此两者都会在同一内存上调用delete,但应使用
release()避免
关键是它不能保证,
memcpy
ing非POD是UB,不管您可能采取什么预防措施。@UldisK:
release
可能(也可能不)使未定义的行为减少灾难性,但是它不会使它有更好的定义。
unique\u ptr
肯定会出问题。它可能必须引用单个内存对象,这样两个对象都会在同一内存上调用delete,但是应该使用
release()来避免它
关键是它不能保证,
memcpy
ing非POD是UB,不管您可能采取什么预防措施。@UldisK:
release
可能(也可能不)使未定义的行为减少灾难性,但这不会使它有更好的定义。这不会对他正在做的
unique\u ptr
做同样的工作。我的印象是,由于我删除了复制构造函数,所以不应该创建默认的移动构造函数。@BenjaminLindley:的确;它将移动指针,而不是执行某种可能导致指针悬空的未定义行为。@UldisK:如果声明了复制构造函数(已删除或其他),则不会隐式生成移动构造函数。它仍然可以显式生成,如下图所示。@BenjaminLindley:你说得对,我没有发现后面的指针。这让事情变得更糟糕。这不会像他那样对
unique\u ptr
做同样的工作。我的印象是,由于我删除了复制构造函数,所以不应该创建默认的移动构造函数。@BenjaminLindley:的确;它将移动指针,而不是执行某种可能导致指针悬空的未定义行为。@UldisK:如果声明了复制构造函数(已删除或其他),则不会隐式生成移动构造函数。它仍然可以显式生成,如下图所示。@BenjaminLindley:你说得对,我没有发现后面的指针。这让事情变得更加糟糕。
memcpy(&pod,&other.pod,sizeof(pod))的优点是什么超过
pod=other.pod?(答:没有一个)您可能还需要
calc=std::move(other.calc)而不是
其他.calc.release()否则对象将泄漏并
计算->重新绑定(此);