簡體   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