繁体   English   中英

重用复制和交换习语

[英]reusing the copy-and-swap idiom

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

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

我打算通过 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";
    }
};

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

Foo x;
Foo y;
x = y;

这只会打印两次“default”,既不打印“copy”也不打印“swap”。 我在这里想念什么?

这个:

 Derived& operator=(Derived copy)

没有为基础 class 声明复制赋值运算符(它的签名错误)。 所以Foo中默认生成的赋值运算符不会使用这个运算符。

记住 12.8:

用户声明的复制赋值运算符 X::operator= 是 class X 的非静态非模板成员 function,其中只有一个类型为 X、X&、const X&、volatile X& 或 const volatile X& 的参数。)重载赋值运算符必须声明为只有一个参数; 见 13.5.3。 ] [注意:可以为 class 声明一种以上形式的复制赋值运算符。 ] [注意:如果 class X 仅具有带有 X& 类型参数的复制赋值运算符,则无法将 const X 类型的表达式分配给 X 类型的 object。

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

你可以做:

 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)); } };

但是你失去了潜在的复制省略优化。

实际上,这将分配两次派生类的成员:一次通过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
}

故事的寓意:不要为复制和交换习语写 CRTP class

如果 memory 正确服务,则不能将赋值运算符作为特殊情况继承。 我相信他们可以在需要时明确using 'd in。

此外,请注意过度使用复制和交换。 它会产生不理想的结果,其中原始文件具有可重复用于制作副本的资源,例如容器。 安全得到保证,但最佳性能却没有。

编译器会自动为 Foo 生成一个复制赋值运算符,因为没有。 如果你添加一个

    using copy_and_swap<Foo>::operator=;

到 Foo 你会看到一个错误告诉你关于 g++ 的歧义。

也许你可以重写它,使它看起来像这样:

template<class Derived>
struct CopySwap
{
  Dervied &operator=(Derived const &other)
  {
    return AssignImpl(other);
  }

  Derived &operator=(Dervied &&other)
  {
    return AssignImpl(std::move(other));
  }

private:
  Derived &AssignImpl(Derived other)
  {
    auto self(static_cast<Derived*>(this));
    self->swap(other);
    return *self;
  }
};

它可能会全部内联,并且可能不会比原始代码慢。

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

无论您做什么,您都处于以下两种情况之一:

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

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

Copy-And-Swap 仅用于非常特定的类。 我不认为这是值得的。

这并不能真正回答问题( @Alexandre C. 已经回答了),但是如果你反转 inheritance,你可以让它工作:

template<typename Base>
struct copy_and_swap : Base
{
    copy_and_swap& operator=(copy_and_swap copy)
    {
        swap(copy);
        return *this;
    }
};

struct Foo_
{
    Foo_()
    {
        std::cout << "default\n";
    }

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

    void swap(Foo_& other)
    {
        std::cout << "swap\n";
    }
};

typedef copy_and_swap<Foo_> Foo;

int main()
{
    Foo x;
    Foo y;
    x = y;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM