重用复制和交换习语

fre*_*low 11 c++ mixins crtp assignment-operator copy-and-swap

我正在尝试将复制和交换习惯用法放入可重用的混音中:

template<typename Derived>
struct copy_and_swap
{
    Derived& operator=(Derived copy)
    {
        Derived* derived = static_cast<Derived*>(this);
        derived->swap(copy);
        return *derived;
    }
};
Run Code Online (Sandbox Code Playgroud)

我打算通过CRTP混合:

struct Foo : copy_and_swap<Foo>
{
    Foo()
    {
        std::cout << "default\n";
    }

    Foo(const Foo& other)
    {
        std::cout << "copy\n";
    }

    void swap(Foo& other)
    {
        std::cout << "swap\n";
    }
};
Run Code Online (Sandbox Code Playgroud)

但是,一个简单的测试显示它不起作用:

Foo x;
Foo y;
x = y;
Run Code Online (Sandbox Code Playgroud)

这仅打印两次"默认",不打印"复制"或"交换".我在这里错过了什么?

Ale*_* C. 7

这个:

 Derived& operator=(Derived copy)
Run Code Online (Sandbox Code Playgroud)

为基类声明复制赋值运算符(它具有错误的签名).因此默认生成的赋值运算符Foo将不使用此运算符.

记住12.8:

用户声明的复制赋值运算符X :: operator =是类X的非静态非模板成员函数,其中只有一个参数类型为X,X&,const X&,volatile X&或const volatile X&.)[注意:必须将重载赋值运算符声明为只有一个参数; 见13.5.3.] [注意:可以为类声明多种形式的复制赋值运算符.] [注意:如果类X只有一个类型为X&的参数的复制赋值运算符,则不能将类型为const X的表达式赋给类型为X的对象.

编辑不这样做(你明白为什么?):

你可以做:

template<typename Derived>
struct copy_and_swap
{
    void operator=(const copy_and_swap& copy)
    {
        Derived copy(static_cast<const Derived&>(copy));
        copy.swap(static_cast<Derived&>(*this));
    }
};
Run Code Online (Sandbox Code Playgroud)

但是你失去了潜在的复制优化.

实际上,这将分配两次派生类的成员:一次通过copy_and_swap<Derived>赋值运算符,一次通过派生类'生成赋值运算符.要纠正这种情况,你必须做(并且不要忘记这样做):

struct Foo : copy_and_swap<Foo>
{

    Foo& operator=(const Foo& x)
    {
        static_cast<copy_and_swap<Foo>&>(*this) = x;
        return *this;
    }

private:
    // Some stateful members here
}
Run Code Online (Sandbox Code Playgroud)

故事的寓意:不要为复制和交换习语写一个CRTP类.


Mat*_* M. 0

恐怕这是需要宏的一个领域,因为自动生成的复制和赋值运算符的规则很复杂。

无论你做什么,你都会遇到以下两种情况:

  • 您已经(显式)提供了赋值运算符的声明,在这种情况下,您也应该提供定义
  • 您尚未(显式)提供赋值运算符的声明,在这种情况下,如果基类非静态成员有可用的赋值运算符,编译器将生成一个声明。

因此,下一个问题是:自动化这样的写作是否值得?

复制和交换仅用于非常特定的类。我认为这不值得。