diff --git a/include/boost/beast/http/fields.hpp b/include/boost/beast/http/fields.hpp index 2a8982f0..4203265f 100644 --- a/include/boost/beast/http/fields.hpp +++ b/include/boost/beast/http/fields.hpp @@ -204,6 +204,15 @@ private: using alloc_traits = beast::detail::allocator_traits; + using pocma = typename + alloc_traits::propagate_on_container_move_assignment; + + using pocca = typename + alloc_traits::propagate_on_container_copy_assignment; + + using pocs = typename + alloc_traits::propagate_on_container_swap; + using size_type = typename beast::detail::allocator_traits::size_type; @@ -265,7 +274,7 @@ public: as if constructed using the same allocator. */ basic_fields& operator=(basic_fields&&) noexcept( - alloc_traits::propagate_on_container_move_assignment::value); + pocma::value && std::is_nothrow_move_assignable::value); /// Copy assignment. basic_fields& operator=(basic_fields const&); diff --git a/include/boost/beast/http/impl/fields.hpp b/include/boost/beast/http/impl/fields.hpp index b97d49c2..48b6ef0c 100644 --- a/include/boost/beast/http/impl/fields.hpp +++ b/include/boost/beast/http/impl/fields.hpp @@ -23,8 +23,6 @@ #include #include #include -#include -#include namespace boost { namespace beast { @@ -434,15 +432,12 @@ template auto basic_fields:: operator=(basic_fields&& other) noexcept( - alloc_traits::propagate_on_container_move_assignment::value) - -> basic_fields& + pocma::value && std::is_nothrow_move_assignable::value) + -> basic_fields& { - static_assert(is_nothrow_move_assignable::value, - "Allocator must be noexcept assignable."); if(this == &other) return *this; - move_assign(other, std::integral_constant{}); + move_assign(other, pocma{}); return *this; } @@ -452,8 +447,7 @@ basic_fields:: operator=(basic_fields const& other) -> basic_fields& { - copy_assign(other, std::integral_constant{}); + copy_assign(other, pocca{}); return *this; } @@ -653,8 +647,7 @@ void basic_fields:: swap(basic_fields& other) { - swap(other, std::integral_constant{}); + swap(other, pocs{}); } template @@ -1124,13 +1117,13 @@ basic_fields:: move_assign(basic_fields& other, std::true_type) { clear_all(); + this->get() = std::move(other.get()); set_ = std::move(other.set_); list_ = std::move(other.list_); method_ = other.method_; target_or_reason_ = other.target_or_reason_; other.method_ = {}; other.target_or_reason_ = {}; - this->get() = other.get(); } template diff --git a/test/beast/http/fields.cpp b/test/beast/http/fields.cpp index 03dab6da..e555f822 100644 --- a/test/beast/http/fields.cpp +++ b/test/beast/http/fields.cpp @@ -28,62 +28,13 @@ public: static constexpr std::size_t max_static_buffer = sizeof(beast::detail::temporary_buffer); - template - class test_allocator - { - public: - using value_type = T; - - test_allocator() noexcept(false) {} - - template::value>::type> - test_allocator(test_allocator const&) noexcept {} - - value_type* - allocate(std::size_t n) - { - return static_cast(::operator new (n*sizeof(value_type))); - } - - void - deallocate(value_type* p, std::size_t) noexcept - { - ::operator delete(p); - } - - template - friend - bool - operator==(test_allocator const&, test_allocator const&) noexcept - { - return true; - } - - template - friend - bool - operator!=(test_allocator const& x, test_allocator const& y) noexcept - { - return !(x == y); - } - }; - - using test_fields = basic_fields>; - BOOST_STATIC_ASSERT(is_fields::value); - BOOST_STATIC_ASSERT(is_fields::value); // std::allocator is noexcept movable, fields should satisfy // these constraints as well. BOOST_STATIC_ASSERT(std::is_nothrow_move_constructible::value); BOOST_STATIC_ASSERT(std::is_nothrow_move_assignable::value); - // Check if basic_fields respects throw-constructibility and - // propagate_on_container_move_assignment of the allocator. - BOOST_STATIC_ASSERT(std::is_nothrow_move_constructible::value); - BOOST_STATIC_ASSERT(!std::is_nothrow_move_assignable::value); - template using fa_t = basic_fields; @@ -1072,6 +1023,72 @@ public: BOOST_STATIC_ASSERT(( insert_test::value)); } + template + class throwing_allocator + { + public: + using value_type = T; + + throwing_allocator() noexcept(false) {} + + throwing_allocator(throwing_allocator const&) noexcept(false) {} + throwing_allocator(throwing_allocator&&) noexcept(false) {} + + throwing_allocator& operator=(throwing_allocator const&) noexcept(false) { return *this; } + throwing_allocator& operator=(throwing_allocator&&) noexcept(false) { return *this; } + + template::value>::type> + throwing_allocator(throwing_allocator const&) noexcept(false) {} + + value_type* + allocate(std::size_t n) + { + return static_cast(::operator new (n*sizeof(value_type))); + } + + void + deallocate(value_type* p, std::size_t) noexcept + { + ::operator delete(p); + } + + template + friend + bool + operator==(throwing_allocator const&, throwing_allocator const&) noexcept + { + return true; + } + + template + friend + bool + operator!=(throwing_allocator const& x, throwing_allocator const& y) noexcept + { + return !(x == y); + } + }; + + void + testIssue2517() + { + using test_fields = basic_fields>; + BOOST_STATIC_ASSERT(is_fields::value); + + // Check if basic_fields respects throw-constructibility and + // propagate_on_container_move_assignment of the allocator. + BOOST_STATIC_ASSERT(std::is_nothrow_move_constructible::value); + BOOST_STATIC_ASSERT(!std::is_nothrow_move_assignable::value); + + test_fields f1; + f1.insert("1", "1"); + test_fields f2; + f2 = std::move(f1); + BEAST_EXPECT(f1.begin() == f1.end()); + BEAST_EXPECT(f2["1"] == "1"); + } + void testEmpty() { @@ -1103,6 +1120,7 @@ public: testIssue1828(); boost::ignore_unused(&fields_test::testIssue2085); + testIssue2517(); testEmpty(); } };