簡體   English   中英

C ++統一分配運算符移動語義

[英]C++ Unified Assignment Operator move-semantics

編輯:解決看到評論 - 不知道如何標記解決與答案。

在c ++ 0x中觀看有關完美轉發/移動語義的第9頻道視頻之后,我認為這是編寫新賦值運算符的好方法。

#include <string>
#include <vector>
#include <iostream>

struct my_type 
{
    my_type(std::string name_)
            :    name(name_)
            {}

    my_type(const my_type&)=default;

    my_type(my_type&& other)
    {
            this->swap(other);
    }

    my_type &operator=(my_type other)
    {
            swap(other);
            return *this;
    }

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
    void operator=(const my_type&)=delete;  
    void operator=(my_type&&)=delete;
};


int main()
{
    my_type t("hello world");
    my_type t1("foo bar");
    t=t1;
    t=std::move(t1);
}

這應該允許將r值和const分配給它。 通過使用適當的構造函數構造一個新對象,然后使用* this交換內容。 這對我來說聽起來很合理,因為沒有數據被復制超過它需要的數量。 指針算術很便宜。

但是我的編譯器不同意。 (g ++ 4.6)我得到了這些錯誤。

copyconsttest.cpp: In function ‘int main()’:
copyconsttest.cpp:40:4: error: ambiguous overload for ‘operator=’ in ‘t = t1’
copyconsttest.cpp:40:4: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <near match>
copyconsttest.cpp:31:11: note:   no known conversion for argument 1 from ‘my_type’ to ‘my_type&&’
copyconsttest.cpp:41:16: error: ambiguous overload for ‘operator=’ in ‘t = std::move [with _Tp = my_type&, typename std::remove_reference< <template-parameter-1-1> >::type = my_type]((* & t1))’
copyconsttest.cpp:41:16: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <deleted>

難道我做錯了什么? 這是不好的做法(我認為沒有辦法測試你是否自我分配)? 編譯器還沒准備好嗎?

謝謝

對副本/交換分配習慣用語非常謹慎。 它可能是次優的,特別是在沒有仔細分析的情況下應用時。 即使您對賦值運算符需要強大的異常安全性,也可以以其他方式獲得該功能。

對於你的例子,我建議:

struct my_type 
{
    my_type(std::string name_)
            :    name(std::move(name_))
            {}

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

這將獲得隱式復制和移動語義,這些語義轉發到std :: string的復制和移動成員。 std :: string的作者最了解如何完成這些操作。

如果您的編譯器尚不支持隱式移動生成,但支持默認的特殊成員,則可以執行以下操作:

struct my_type 
{
    my_type(std::string name_)
            :    name(std::move(name_))
            {}

    my_type(const mytype&) = default;
    my_type& operator=(const mytype&) = default;
    my_type(mytype&&) = default;
    my_type& operator=(mytype&&) = default;

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

如果您只是想明確您的特殊成員,您也可以選擇執行上述操作。

如果您正在處理尚未支持默認特殊成員(或隱式移動成員)的編譯器,那么您可以顯式提供編譯器在完全符合C ++ 11時最終應該默認的內容:

struct my_type 
{
    my_type(std::string name_)
            :    name(std::move(name_))
            {}

    my_type(const mytype& other)
        : name(other.name) {}
    my_type& operator=(const mytype& other)
    {
        name = other.name;
        return *this;
    }
    my_type(mytype&& other)
        : name(std::move(other.name)) {}
    my_type& operator=(mytype&& other)
    {
        name = std::move(other.name);
        return *this;
    }

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

如果你真的需要強大的異常安全性,可以設計一次並明確它(編輯包括Luc Danton的建議):

template <class C>
typename std::enable_if
<
    std::is_nothrow_move_assignable<C>::value,
    C&
>::type
strong_assign(C& c, C other)
{
    c = std::move(other);
    return c;
}

template <class C>
typename std::enable_if
<
    !std::is_nothrow_move_assignable<C>::value,
    C&
>::type
strong_assign(C& c, C other)
{
    using std::swap;
    static_assert(std::is_nothrow_swappable_v<C>,  // C++17 only
                  "Not safe if you move other into this function");
    swap(c, other);
    return c;
}

現在,您的客戶可以使用strong_assign在效率(我的類型::運算符=)或強異常安全性之間進行strong_assign

您是否仔細閱讀了錯誤消息? 它會看到兩個錯誤,即您有多個復制賦值運算符和多個移動賦值運算符。 這是完全正確的!

特殊成員必須最多指定一次,無論它們是否被默認,刪除,按常規定義,或通過被忽略而隱式處理。 你有兩個拷貝賦值運算符(一個采用my_type ,另一個采用my_type const & )和兩個運動賦值運算符(一個采用my_type ,另一個采用my_type && )。 請注意,采用my_type的賦值運算符可以處理左值和右值引用,因此它既可以作為復制賦值,也可以作為移動賦值。

大多數特殊成員的功能簽名有多種形式。 你必須選一個; 你不能使用一個不尋常的,然后刪除傳統的,因為這將是一個雙重聲明。 編譯器將自動使用異常形成的特殊成員,並且不會使用傳統簽名合成特殊成員。

(請注意,錯誤提到了三個候選。對於每個分配類型,它會看到相應的已刪除方法,采用my_type的方法,然后將另一個已刪除的方法視為緊急匹配。)

你應該刪除賦值運算符的那些重載嗎? 您的賦值運算符聲明不應該是模板或其他東西嗎? 我真的不明白這是怎么回事。

請注意,即使這樣可行,通過以這種方式實現移動賦值運算符,剛剛移動的對象所擁有的資源將在其生命周期結束時釋放,而不是在賦值時釋放。 有關詳細信息,請參見此處

http://cpp-next.com/archive/2009/09/your-next-assignment/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM