Merge unordered from trunk.

Add `BOOST_NOEXPECT` to:

- Move constructors (when appropriate)
- Destructors
- Iterators

Also, fix some misleading documentation about the containers' move support.


[SVN r85048]
This commit is contained in:
Daniel James
2013-07-15 21:32:45 +00:00
parent 0c7c7cc6ad
commit c88126e1d2
6 changed files with 173 additions and 30 deletions

View File

@ -8,11 +8,12 @@
Support for move semantics is implemented using Boost.Move. If rvalue Support for move semantics is implemented using Boost.Move. If rvalue
references are available it will use them, but if not it uses a close, references are available it will use them, but if not it uses a close,
but imperfect emulation. On such compilers you'll need to use Boost.Move but imperfect emulation. On such compilers:
to take advantage of using movable container elements, also note that:
* Non-copyable objects can be stored in the containers, but without support * Non-copyable objects can be stored in the containers.
for rvalue references the container will not be movable. They can be constructed in place using `emplace`, or if they support
Boost.Move, moved into place.
* The containers themselves are not movable.
* Argument forwarding is not perfect. * Argument forwarding is not perfect.
[endsect] [endsect]

View File

@ -73,9 +73,9 @@ namespace boost { namespace unordered { namespace iterator_detail {
typedef typename Node::value_type value_type; typedef typename Node::value_type value_type;
l_iterator() : ptr_() {} l_iterator() BOOST_NOEXCEPT : ptr_() {}
l_iterator(iterator x, std::size_t b, std::size_t c) l_iterator(iterator x, std::size_t b, std::size_t c) BOOST_NOEXCEPT
: ptr_(x.node_), bucket_(b), bucket_count_(c) {} : ptr_(x.node_), bucket_(b), bucket_count_(c) {}
value_type& operator*() const { value_type& operator*() const {
@ -100,11 +100,11 @@ namespace boost { namespace unordered { namespace iterator_detail {
return tmp; return tmp;
} }
bool operator==(l_iterator x) const { bool operator==(l_iterator x) const BOOST_NOEXCEPT {
return ptr_ == x.ptr_; return ptr_ == x.ptr_;
} }
bool operator!=(l_iterator x) const { bool operator!=(l_iterator x) const BOOST_NOEXCEPT {
return ptr_ != x.ptr_; return ptr_ != x.ptr_;
} }
}; };
@ -132,13 +132,13 @@ namespace boost { namespace unordered { namespace iterator_detail {
typedef typename Node::value_type value_type; typedef typename Node::value_type value_type;
cl_iterator() : ptr_() {} cl_iterator() BOOST_NOEXCEPT : ptr_() {}
cl_iterator(iterator x, std::size_t b, std::size_t c) : cl_iterator(iterator x, std::size_t b, std::size_t c) BOOST_NOEXCEPT :
ptr_(x.node_), bucket_(b), bucket_count_(c) {} ptr_(x.node_), bucket_(b), bucket_count_(c) {}
cl_iterator(boost::unordered::iterator_detail::l_iterator< cl_iterator(boost::unordered::iterator_detail::l_iterator<
Node, Policy> const& x) : Node, Policy> const& x) BOOST_NOEXCEPT :
ptr_(x.ptr_), bucket_(x.bucket_), bucket_count_(x.bucket_count_) ptr_(x.ptr_), bucket_(x.bucket_), bucket_count_(x.bucket_count_)
{} {}
@ -164,11 +164,15 @@ namespace boost { namespace unordered { namespace iterator_detail {
return tmp; return tmp;
} }
friend bool operator==(cl_iterator const& x, cl_iterator const& y) { friend bool operator==(cl_iterator const& x, cl_iterator const& y)
BOOST_NOEXCEPT
{
return x.ptr_ == y.ptr_; return x.ptr_ == y.ptr_;
} }
friend bool operator!=(cl_iterator const& x, cl_iterator const& y) { friend bool operator!=(cl_iterator const& x, cl_iterator const& y)
BOOST_NOEXCEPT
{
return x.ptr_ != y.ptr_; return x.ptr_ != y.ptr_;
} }
}; };
@ -204,9 +208,9 @@ namespace boost { namespace unordered { namespace iterator_detail {
typedef typename Node::value_type value_type; typedef typename Node::value_type value_type;
iterator() : node_() {} iterator() BOOST_NOEXCEPT : node_() {}
explicit iterator(typename Node::link_pointer x) : explicit iterator(typename Node::link_pointer x) BOOST_NOEXCEPT :
node_(static_cast<node_pointer>(x)) {} node_(static_cast<node_pointer>(x)) {}
value_type& operator*() const { value_type& operator*() const {
@ -228,11 +232,11 @@ namespace boost { namespace unordered { namespace iterator_detail {
return tmp; return tmp;
} }
bool operator==(iterator const& x) const { bool operator==(iterator const& x) const BOOST_NOEXCEPT {
return node_ == x.node_; return node_ == x.node_;
} }
bool operator!=(iterator const& x) const { bool operator!=(iterator const& x) const BOOST_NOEXCEPT {
return node_ != x.node_; return node_ != x.node_;
} }
}; };
@ -266,12 +270,12 @@ namespace boost { namespace unordered { namespace iterator_detail {
typedef typename Node::value_type value_type; typedef typename Node::value_type value_type;
c_iterator() : node_() {} c_iterator() BOOST_NOEXCEPT : node_() {}
explicit c_iterator(typename Node::link_pointer x) : explicit c_iterator(typename Node::link_pointer x) BOOST_NOEXCEPT :
node_(static_cast<node_pointer>(x)) {} node_(static_cast<node_pointer>(x)) {}
c_iterator(iterator const& x) : node_(x.node_) {} c_iterator(iterator const& x) BOOST_NOEXCEPT : node_(x.node_) {}
value_type const& operator*() const { value_type const& operator*() const {
return node_->value(); return node_->value();
@ -292,11 +296,15 @@ namespace boost { namespace unordered { namespace iterator_detail {
return tmp; return tmp;
} }
friend bool operator==(c_iterator const& x, c_iterator const& y) { friend bool operator==(c_iterator const& x, c_iterator const& y)
BOOST_NOEXCEPT
{
return x.node_ == y.node_; return x.node_ == y.node_;
} }
friend bool operator!=(c_iterator const& x, c_iterator const& y) { friend bool operator!=(c_iterator const& x, c_iterator const& y)
BOOST_NOEXCEPT
{
return x.node_ != y.node_; return x.node_ != y.node_;
} }
}; };

View File

@ -117,11 +117,13 @@ namespace unordered
#if defined(BOOST_UNORDERED_USE_MOVE) #if defined(BOOST_UNORDERED_USE_MOVE)
unordered_map(BOOST_RV_REF(unordered_map) other) unordered_map(BOOST_RV_REF(unordered_map) other)
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
: table_(other.table_, boost::unordered::detail::move_tag()) : table_(other.table_, boost::unordered::detail::move_tag())
{ {
} }
#elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
unordered_map(unordered_map&& other) unordered_map(unordered_map&& other)
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
: table_(other.table_, boost::unordered::detail::move_tag()) : table_(other.table_, boost::unordered::detail::move_tag())
{ {
} }
@ -142,7 +144,7 @@ namespace unordered
// Destructor // Destructor
~unordered_map(); ~unordered_map() BOOST_NOEXCEPT;
// Assign // Assign
@ -598,11 +600,13 @@ namespace unordered
#if defined(BOOST_UNORDERED_USE_MOVE) #if defined(BOOST_UNORDERED_USE_MOVE)
unordered_multimap(BOOST_RV_REF(unordered_multimap) other) unordered_multimap(BOOST_RV_REF(unordered_multimap) other)
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
: table_(other.table_, boost::unordered::detail::move_tag()) : table_(other.table_, boost::unordered::detail::move_tag())
{ {
} }
#elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
unordered_multimap(unordered_multimap&& other) unordered_multimap(unordered_multimap&& other)
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
: table_(other.table_, boost::unordered::detail::move_tag()) : table_(other.table_, boost::unordered::detail::move_tag())
{ {
} }
@ -623,7 +627,7 @@ namespace unordered
// Destructor // Destructor
~unordered_multimap(); ~unordered_multimap() BOOST_NOEXCEPT;
// Assign // Assign
@ -1057,7 +1061,7 @@ namespace unordered
} }
template <class K, class T, class H, class P, class A> template <class K, class T, class H, class P, class A>
unordered_map<K,T,H,P,A>::~unordered_map() {} unordered_map<K,T,H,P,A>::~unordered_map() BOOST_NOEXCEPT {}
template <class K, class T, class H, class P, class A> template <class K, class T, class H, class P, class A>
unordered_map<K,T,H,P,A>::unordered_map( unordered_map<K,T,H,P,A>::unordered_map(
@ -1390,7 +1394,7 @@ namespace unordered
} }
template <class K, class T, class H, class P, class A> template <class K, class T, class H, class P, class A>
unordered_multimap<K,T,H,P,A>::~unordered_multimap() {} unordered_multimap<K,T,H,P,A>::~unordered_multimap() BOOST_NOEXCEPT {}
template <class K, class T, class H, class P, class A> template <class K, class T, class H, class P, class A>
unordered_multimap<K,T,H,P,A>::unordered_multimap( unordered_multimap<K,T,H,P,A>::unordered_multimap(

View File

@ -115,11 +115,13 @@ namespace unordered
#if defined(BOOST_UNORDERED_USE_MOVE) #if defined(BOOST_UNORDERED_USE_MOVE)
unordered_set(BOOST_RV_REF(unordered_set) other) unordered_set(BOOST_RV_REF(unordered_set) other)
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
: table_(other.table_, boost::unordered::detail::move_tag()) : table_(other.table_, boost::unordered::detail::move_tag())
{ {
} }
#elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
unordered_set(unordered_set&& other) unordered_set(unordered_set&& other)
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
: table_(other.table_, boost::unordered::detail::move_tag()) : table_(other.table_, boost::unordered::detail::move_tag())
{ {
} }
@ -140,7 +142,7 @@ namespace unordered
// Destructor // Destructor
~unordered_set(); ~unordered_set() BOOST_NOEXCEPT;
// Assign // Assign
@ -582,11 +584,13 @@ namespace unordered
#if defined(BOOST_UNORDERED_USE_MOVE) #if defined(BOOST_UNORDERED_USE_MOVE)
unordered_multiset(BOOST_RV_REF(unordered_multiset) other) unordered_multiset(BOOST_RV_REF(unordered_multiset) other)
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
: table_(other.table_, boost::unordered::detail::move_tag()) : table_(other.table_, boost::unordered::detail::move_tag())
{ {
} }
#elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #elif !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
unordered_multiset(unordered_multiset&& other) unordered_multiset(unordered_multiset&& other)
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
: table_(other.table_, boost::unordered::detail::move_tag()) : table_(other.table_, boost::unordered::detail::move_tag())
{ {
} }
@ -607,7 +611,7 @@ namespace unordered
// Destructor // Destructor
~unordered_multiset(); ~unordered_multiset() BOOST_NOEXCEPT;
// Assign // Assign
@ -1032,7 +1036,7 @@ namespace unordered
} }
template <class T, class H, class P, class A> template <class T, class H, class P, class A>
unordered_set<T,H,P,A>::~unordered_set() {} unordered_set<T,H,P,A>::~unordered_set() BOOST_NOEXCEPT {}
template <class T, class H, class P, class A> template <class T, class H, class P, class A>
unordered_set<T,H,P,A>::unordered_set( unordered_set<T,H,P,A>::unordered_set(
@ -1316,7 +1320,7 @@ namespace unordered
} }
template <class T, class H, class P, class A> template <class T, class H, class P, class A>
unordered_multiset<T,H,P,A>::~unordered_multiset() {} unordered_multiset<T,H,P,A>::~unordered_multiset() BOOST_NOEXCEPT {}
template <class T, class H, class P, class A> template <class T, class H, class P, class A>
unordered_multiset<T,H,P,A>::unordered_multiset( unordered_multiset<T,H,P,A>::unordered_multiset(

View File

@ -25,6 +25,7 @@ test-suite unordered
[ run minimal_allocator.cpp ] [ run minimal_allocator.cpp ]
[ run compile_set.cpp ] [ run compile_set.cpp ]
[ run compile_map.cpp ] [ run compile_map.cpp ]
[ run noexcept_tests.cpp ]
[ run link_test_1.cpp link_test_2.cpp ] [ run link_test_1.cpp link_test_2.cpp ]
[ run incomplete_test.cpp ] [ run incomplete_test.cpp ]
[ run simple_tests.cpp ] [ run simple_tests.cpp ]

View File

@ -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)
#include "../helpers/prefix.hpp"
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#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<int>
{
hash_possible_exception(hash_possible_exception const&) {}
};
struct equal_to_possible_exception : std::equal_to<int>
{
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<int> >::value));
BOOST_TEST((boost::is_nothrow_move_constructible<
boost::unordered_multiset<int> >::value));
BOOST_TEST((boost::is_nothrow_move_constructible<
boost::unordered_map<int, int> >::value));
BOOST_TEST((boost::is_nothrow_move_constructible<
boost::unordered_multimap<int, int> >::value));
#endif
BOOST_TEST((!boost::is_nothrow_move_constructible<
boost::unordered_set<int, hash_possible_exception>
>::value));
BOOST_TEST((!boost::is_nothrow_move_constructible<
boost::unordered_multiset<int, boost::hash<int>,
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<int>
{
BOOST_COPYABLE_AND_MOVABLE(hash_nothrow_move)
typedef boost::hash<int> 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<base const&>(*this)(x); }
};
class equal_to_nothrow_move : std::equal_to<int>
{
BOOST_COPYABLE_AND_MOVABLE(equal_to_nothrow_move)
typedef std::equal_to<int> 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<base const&>(*this)(x, y); }
};
UNORDERED_AUTO_TEST(test_no_throw_when_noexcept)
{
typedef boost::unordered_set<int,
hash_nothrow_move, equal_to_nothrow_move> throwing_set;
if (boost::is_nothrow_move_constructible<throwing_set>::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()