Can I force a special member function to be insecure by default?

The following structure cannot compile under C ++ 11 due to the fact that I declared the move destination operator as noexcept :

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

The default assignment operator generated by the compiler is noexcept(false) because std::vector<int> the destination move is also equal to noexcept(false) . This, in turn, is due to the fact that the default distributor std::allocator_traits<T>:: propagate_on_container_move_assignment set to std::false_type . See also this question .

I believe this has been fixed in C ++ 14 (see library defect 2103 ).

My question is, is there a way to get me noexcept use the default assignment operator without having to define it myself?

If this is not possible, is there a way I can trick std::vector<int> into noexcept move the assignable so that noexcept(true) passed to my structure?

+8
c ++ c ++ 11 noexcept
source share
2 answers

I believe this is fixed in C ++ 14 (see library defect 2103).

As a DR, this fix should be considered a fix for C ++ 11, so some C ++ 11 implementations have already fixed it.

My question is, is there a way to get me noexcept use the default assignment operator without having to define it myself?

In order for the default assignment operator to be noexcept , you need to make your noexcept sub-objects move the assignment operators.

The most obvious portable way I can think of is to use a wrapper around std::vector , which makes the move be 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; }; 

Another similar option is to define your own type of distributor using the DR 2013 fix and use it:

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

Another option is to use a standard library implementation, such as GCC, which implements permission for DR 2013, and also makes the std::vector move assign assign noexcept for other types of allocators, when it is known that all instances of the allocator are compared equal.

+4
source share

I donโ€™t think you can force something, but you can wrap it:

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

(Thanks to Jonathan Wackel)

+1
source share

All Articles