C++ 使用宏覆盖类move函数接口的缺点

C++ 使用宏覆盖类move函数接口的缺点,c++,macros,c++14,move-semantics,C++,Macros,C++14,Move Semantics,使用宏作为所有移动语义的接口有哪些缺点 我最近读过一篇关于类()中移动相关函数的语法的文章,在我看来,这是非常乏味和重复的:- C(C&&) = default; // Move constructor C& operator=(C&&) & = default; // Move assignment operator 如果我想用同一个实现自定义移动赋值和移动构造函数,那就更麻烦了。(与问题相同)

使用宏作为所有移动语义的接口有哪些缺点

我最近读过一篇关于类()中移动相关函数的语法的文章,在我看来,这是非常乏味和重复的:-

C(C&&) = default;                    // Move constructor
C& operator=(C&&) & = default;       // Move assignment operator
如果我想用同一个实现自定义移动赋值和移动构造函数,那就更麻烦了。(与问题相同)

因此,我决定创建一个宏(类似交换的函数),使所有内容在一个地方简洁明了

#define MACRO_MOVE(C)                        \
C& operator=(C&& that) &  {                 \
     moveHack(this,&that);  return *this;    \
}                                            \
C(C&& that){                                 \
     moveHack(this,&that);                   \
}                                            \
static void moveHack(C* dst,C* src)
用法如下:-

class X{
    int data=42;
    MACRO_MOVE(X){
        dst->data=src->data;
        src->data=0;
    }
};
然而,我以前从未见过有人这样做。
有什么我应该关心的问题吗?

这种方法有什么具体的缺点吗

零规则是,只有管理资源(如内存)的类才需要显式的move/copy赋值/构造方法

您可以将几乎所有类型的存储组合到自我管理的资源中。这些资源类型往往是几个不同的变体(值指针、唯一指针、共享指针等)之一

通常在移动分配时,还需要清理现有状态。移动构造时不是这样。这两个实现可以共享一些代码,但通常您不希望它们共享

对一些简单的资源管理类型进行这种宏黑客攻击是个坏主意。到处都有复杂的资源管理类型是个坏主意。这种宏黑客不处理移动和分配这两种根本不同的操作。一般来说,只有在避免宏的成本很高时才应使用宏

您的宏没有添加太多实用程序。它所做的事情看起来容易出现bug,而且很危险。如果你不得不调试它,你会得到不可调试的代码

因此,有很多理由不使用该宏,很少有理由使用它


建设、分配和破坏是联系在一起的。任务可以天真地被看作是破坏+构造,但这通常是无效的;而你的连破坏都没有

当移动分配操作符需要释放一些资源时会发生什么?而且,如果不需要这样做,为什么编译器生成的移动赋值操作符不够用?@cdhowie IMHO,移动赋值总是和移动构造函数一样工作,所以我将在
src->data=0
行附近添加这样的代码。(?)那不是真的。在move构造函数中,您知道没有可释放的现有资源。无论哪种方式,只要移动构造函数正确地将自身初始化为不拥有任何内容,您通常都可以将这两种方法实现为
swap(that)
。@cpplower宏混淆了实际发生的事情,特别是对不熟悉您的代码的人而言。一般规则是,只有当宏的使用对清晰度的帮助远远大于有害时,才应该使用宏,我不确定这是否正确。我会提出相反的论点:在这里使用宏会损害可读性(在我看来),因为它基本上对读者隐藏了两个成员声明。问题是,理论上,你应该让编译器生成默认的move特殊函数,如果你的所有成员都已经正确地实现了move语义,那么这应该是正确的。这尤其意味着你永远不会拥有你的类所拥有的裸指针(就像你的例子中的
data
一样),但它们总是被包装在类似
unique\u ptr
的东西中。那当然是理论。