diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 682e3544..e33dcea2 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -117,11 +117,13 @@ namespace unordered #if defined(BOOST_UNORDERED_USE_MOVE) unordered_map(BOOST_RV_REF(unordered_map) other) + BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { } #elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_map(unordered_map&& other) + BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { } @@ -142,7 +144,7 @@ namespace unordered // Destructor - ~unordered_map(); + ~unordered_map() BOOST_NOEXCEPT; // Assign @@ -598,11 +600,13 @@ namespace unordered #if defined(BOOST_UNORDERED_USE_MOVE) unordered_multimap(BOOST_RV_REF(unordered_multimap) other) + BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { } #elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_multimap(unordered_multimap&& other) + BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { } @@ -623,7 +627,7 @@ namespace unordered // Destructor - ~unordered_multimap(); + ~unordered_multimap() BOOST_NOEXCEPT; // Assign @@ -1057,7 +1061,7 @@ namespace unordered } template - unordered_map::~unordered_map() {} + unordered_map::~unordered_map() BOOST_NOEXCEPT {} template unordered_map::unordered_map( @@ -1390,7 +1394,7 @@ namespace unordered } template - unordered_multimap::~unordered_multimap() {} + unordered_multimap::~unordered_multimap() BOOST_NOEXCEPT {} template unordered_multimap::unordered_multimap( diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 0ccf1b94..9ba5002f 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -115,11 +115,13 @@ namespace unordered #if defined(BOOST_UNORDERED_USE_MOVE) unordered_set(BOOST_RV_REF(unordered_set) other) + BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { } #elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_set(unordered_set&& other) + BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { } @@ -140,7 +142,7 @@ namespace unordered // Destructor - ~unordered_set(); + ~unordered_set() BOOST_NOEXCEPT; // Assign @@ -582,11 +584,13 @@ namespace unordered #if defined(BOOST_UNORDERED_USE_MOVE) unordered_multiset(BOOST_RV_REF(unordered_multiset) other) + BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { } #elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) unordered_multiset(unordered_multiset&& other) + BOOST_NOEXCEPT_IF(table::nothrow_move_constructible) : table_(other.table_, boost::unordered::detail::move_tag()) { } @@ -607,7 +611,7 @@ namespace unordered // Destructor - ~unordered_multiset(); + ~unordered_multiset() BOOST_NOEXCEPT; // Assign @@ -1032,7 +1036,7 @@ namespace unordered } template - unordered_set::~unordered_set() {} + unordered_set::~unordered_set() BOOST_NOEXCEPT {} template unordered_set::unordered_set( @@ -1316,7 +1320,7 @@ namespace unordered } template - unordered_multiset::~unordered_multiset() {} + unordered_multiset::~unordered_multiset() BOOST_NOEXCEPT {} template unordered_multiset::unordered_multiset( diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index 581e4ee0..53e0d157 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -25,6 +25,7 @@ test-suite unordered [ run minimal_allocator.cpp ] [ run compile_set.cpp ] [ run compile_map.cpp ] + [ run noexcept_tests.cpp ] [ run link_test_1.cpp link_test_2.cpp ] [ run incomplete_test.cpp ] [ run simple_tests.cpp ] diff --git a/test/unordered/noexcept_tests.cpp b/test/unordered/noexcept_tests.cpp new file mode 100644 index 00000000..19b04dd3 --- /dev/null +++ b/test/unordered/noexcept_tests.cpp @@ -0,0 +1,125 @@ + +// Copyright 2013 Daniel James. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#define private public +#include "../helpers/prefix.hpp" +#include +#include +#include "../helpers/postfix.hpp" + +#include "../helpers/test.hpp" + +namespace noexcept_tests +{ + // Test the noexcept is set correctly for the move constructor. + + struct hash_possible_exception : boost::hash + { + hash_possible_exception(hash_possible_exception const&) {} + }; + + struct equal_to_possible_exception : std::equal_to + { + equal_to_possible_exception(equal_to_possible_exception const&) {} + }; + + UNORDERED_AUTO_TEST(test_noexcept) + { +#if !defined(BOOST_NO_CXX11_NOEXCEPT) + BOOST_TEST((boost::is_nothrow_move_constructible< + boost::unordered_set >::value)); + BOOST_TEST((boost::is_nothrow_move_constructible< + boost::unordered_multiset >::value)); + BOOST_TEST((boost::is_nothrow_move_constructible< + boost::unordered_map >::value)); + BOOST_TEST((boost::is_nothrow_move_constructible< + boost::unordered_multimap >::value)); +#endif + + BOOST_TEST((!boost::is_nothrow_move_constructible< + boost::unordered_set + >::value)); + BOOST_TEST((!boost::is_nothrow_move_constructible< + boost::unordered_multiset, + equal_to_possible_exception> + >::value)); + } + + // Test that the move constructor does actually move without throwing + // an exception when it claims to. + + struct test_exception {}; + + bool throwing_test_exception = false; + void test_throw(char const* name) { + if (throwing_test_exception) { + std::cerr << "Throw exception in: " << name << std::endl; + throw test_exception(); + } + } + + class hash_nothrow_move : boost::hash + { + BOOST_COPYABLE_AND_MOVABLE(hash_nothrow_move) + + typedef boost::hash base; + public: + hash_nothrow_move(BOOST_RV_REF(hash_nothrow_move)) + BOOST_NOEXCEPT {} + + hash_nothrow_move() { test_throw("Constructor"); } + hash_nothrow_move(hash_nothrow_move const& x) { test_throw("Copy"); } + hash_nothrow_move& operator=(hash_nothrow_move const&) + { test_throw("Assign"); return *this; } + std::size_t operator()(int x) const + { test_throw("Operator"); return static_cast(*this)(x); } + }; + + class equal_to_nothrow_move : std::equal_to + { + BOOST_COPYABLE_AND_MOVABLE(equal_to_nothrow_move) + + typedef std::equal_to base; + public: + equal_to_nothrow_move(BOOST_RV_REF(equal_to_nothrow_move)) + BOOST_NOEXCEPT {} + equal_to_nothrow_move() { test_throw("Constructor"); } + equal_to_nothrow_move(equal_to_nothrow_move const& x) + { test_throw("Copy"); } + equal_to_nothrow_move& operator=(equal_to_nothrow_move const&) + { test_throw("Assign"); return *this; } + std::size_t operator()(int x, int y) const + { test_throw("Operator"); return static_cast(*this)(x, y); } + }; + + UNORDERED_AUTO_TEST(test_no_throw_when_noexcept) + { + typedef boost::unordered_set throwing_set; + + if (boost::is_nothrow_move_constructible::value) + { + throwing_test_exception = false; + + throwing_set x1; + x1.insert(10); + x1.insert(50); + + + try { + throwing_test_exception = true; + + throwing_set x2 = boost::move(x1); + BOOST_TEST(x2.size() == 2); + BOOST_TEST(*x2.begin() == 10 || *x2.begin() == 50); + } catch(test_exception) { + BOOST_TEST(false); + } + + throwing_test_exception = false; + } + } +} + +RUN_TESTS()