2013-09-11 31 views
8

以下結構編譯失敗下C++ 11由於這樣的事實,我已聲明的移動賦值運算符作爲noexcept我可以強制默認的特殊成員函數是否爲noexcept?

struct foo 
{ 
    std::vector<int> data; 
    foo& operator=(foo&&) noexcept = default; 
}; 

由編譯器生成的默認移動賦值運算符是noexcept(false)由於這樣的事實那std::vector<int>的移動任務也是noexcept(false)。這又是由於默認分配器std::allocator_traits<T>:: propagate_on_container_move_assignment設置爲std::false_type。另見this question

我相信這已在C++ 14中修復(請參閱library defect 2103)。

我的問題是,有沒有辦法讓我強制noexcept默認的移動賦值賦值操作符,而不必自己定義它?

如果這是不可能的,有沒有一種方法可以欺騙std::vector<int>成爲noexcept移動賦值,以便noexcept(true)被傳遞給我的結構?

+2

是否好,如果'data'是一個'的std ::矢量'其中'A'不是默認的'std :: allocator '? – aschepler

+1

我期待將代碼複雜性保持在最低限度。我期望使用自定義分配器比其他編碼人員更難理解,而不是簡單的非默認移動賦值運算符。 – marack

+1

也就是說,如果'std :: vector '中的'A'是一個簡單的默認分配器的內聯包裝器,它可能會工作... – marack

回答

4

我相信這已在C++ 14中修復(請參閱庫缺陷2103)。

作爲修復的DR應該被視爲C++ 11的修正,因此一些C++ 11實現已經修復它。

我的問題是,有沒有辦法讓我強制noexcept默認移動賦值賦值操作符,而不必自己定義它?

對於默認移動賦值運算符爲noexcept,您需要使其子對象具有noexcept移動賦值運算符。

我能想到的最明顯的可移植的方法是使用一個包裝圍繞std::vector迫使行動是noexcept

template<typename T, typename A = std::allocator<T>> 
    struct Vector : std::vector<T, A> 
    { 
    using vector::vector; 

    Vector& operator=(Vector&& v) noexcept 
    { 
     static_cast<std::vector<T,A>&>(*this) = std::move(v); 
     return *this; 
    } 
    Vector& operator=(const Vector&) = default; 
    }; 

另一個類似的選項是與DR 2013修復定義自己的分配器類型和使用:

template<typename T> 
    struct Allocator : std::allocator<T> 
    { 
    Allocator() = default; 
    template<typename U> Allocator(const Allocator<U>&) { } 
    using propagate_on_container_move_assignment = true_type; 
    template<typename U> struct rebind { using other = Allocator<U>; }; 
    }; 

template<typename T> 
    using Vector = std::vector<T, Allocator<T>>; 

另一種選擇是使用標準庫的實現,如GCC的它實現了分辨率DR 2013,也使得的std::vector移動賦值運算符noexcept其他分配器類型,當知道所有分配器實例比較相等時。

1

我不認爲你可以強制任何東西,但你可能把它包裝:

#include <iostream> 
#include <vector> 

template <typename T> 
struct Wrap 
{ 
    public: 
    Wrap() noexcept 
    { 
     new (m_value) T; 
    } 

    Wrap(const Wrap& other) noexcept 
    { 
     new (m_value) T(std::move(other.value())); 
    } 

    Wrap(Wrap&& other) noexcept 
    { 
     std::swap(value(), other.value()); 
    } 


    Wrap(const T& other) noexcept 
    { 
     new (m_value) T(std::move(other)); 
    } 

    Wrap(T&& other) noexcept 
    { 
     new (m_value) T(std::move(other)); 
    } 

    ~Wrap() noexcept 
    { 
     value().~T(); 
    } 

    Wrap& operator = (const Wrap& other) noexcept 
    { 
     value() = other.value(); 
     return *this; 
    } 

    Wrap& operator = (Wrap&& other) noexcept 
    { 
     value() = std::move(other.value()); 
     return *this; 
    } 

    Wrap& operator = (const T& other) noexcept 
    { 
     value() = other; 
     return *this; 
    } 

    Wrap& operator = (T&& other) noexcept 
    { 
     value() = std::move(other); 
     return *this; 
    } 

    T& value() noexcept { return *reinterpret_cast<T*>(m_value); } 
    const T& value() const noexcept { return *reinterpret_cast<const T*>(m_value); } 
    operator T&() noexcept { return value(); } 
    operator const T&() const noexcept { return value(); } 

    private: 
    typename std::aligned_storage <sizeof(T), std::alignment_of<T>::value>::type m_value[1]; 
}; 


struct Foo 
{ 
    public: 
    Foo& operator = (Foo&&) noexcept = default; 

    std::vector<int>& data() noexcept { return m_data; } 
    const std::vector<int>& data() const noexcept { return m_data; } 

    private: 
    Wrap<std::vector<int>> m_data; 
}; 

int main() { 
    Foo foo; 
    foo.data().push_back(1); 
    Foo boo; 
    boo = std::move(foo); 
    // 01 
    std::cout << foo.data().size() << boo.data().size() << std::endl; 
    return 0; 
} 

(感謝喬納森Wakely)

+1

該類型不可複製,您需要複製ctor和賦值操作。此外,你可以使用'std :: aligned_storage :: type'而不是char數組。 –

相關問題