From d99628981d1ff7181bee652bc5177342d79aaf4e Mon Sep 17 00:00:00 2001 From: Christopher Wecht Date: Fri, 1 Sep 2017 17:53:46 +0200 Subject: [PATCH] push_back: added support for not-copyable but moveable value-types like e.g. unique_ptr --- .../boost/range/algorithm_ext/push_back.hpp | 43 ++++++++++- test/algorithm_ext_test/push_back.cpp | 74 +++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/include/boost/range/algorithm_ext/push_back.hpp b/include/boost/range/algorithm_ext/push_back.hpp index 6fb9b9b..d741b62 100644 --- a/include/boost/range/algorithm_ext/push_back.hpp +++ b/include/boost/range/algorithm_ext/push_back.hpp @@ -20,9 +20,37 @@ namespace boost { - namespace range +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + namespace range_detail { +template < class Container, class Range > +inline Container& push_back_impl( Container& on, Range&& from, std::false_type) +{ + BOOST_RANGE_CONCEPT_ASSERT(( SinglePassRangeConcept )); + BOOST_ASSERT_MSG(!range_detail::is_same_object(on, from), + "cannot move from a container to itself"); + on.insert( on.end(), + std::make_move_iterator(boost::begin(from)), + std::make_move_iterator(boost::end(from))); + return on; +} + +template < class Container, class Range > +inline Container& push_back_impl( Container& on, const Range& from, std::true_type) +{ + BOOST_RANGE_CONCEPT_ASSERT(( SinglePassRangeConcept )); + BOOST_ASSERT_MSG(!range_detail::is_same_object(on, from), + "cannot copy from a container to itself"); + on.insert( on.end(), boost::begin(from), boost::end(from)); + return on; +} + + } //namespace range_detail +#endif + + namespace range { +#if defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) template< class Container, class Range > inline Container& push_back( Container& on, const Range& from ) { @@ -34,6 +62,19 @@ inline Container& push_back( Container& on, const Range& from ) return on; } +#else + +template< class Container, class Range > +inline Container& push_back( Container& on, Range&& from ) +{ + BOOST_RANGE_CONCEPT_ASSERT(( SinglePassRangeConcept )); + range_detail::push_back_impl(on, + std::forward(from), + std::is_lvalue_reference() ); + return on; +} + +#endif } // namespace range using range::push_back; } // namespace boost diff --git a/test/algorithm_ext_test/push_back.cpp b/test/algorithm_ext_test/push_back.cpp index 7e9d539..b3e4754 100644 --- a/test/algorithm_ext_test/push_back.cpp +++ b/test/algorithm_ext_test/push_back.cpp @@ -19,6 +19,7 @@ #include #include + namespace { template< class Container > @@ -58,6 +59,76 @@ namespace test_push_back_impl< std::vector >(); test_push_back_impl< std::list >(); } + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + // test type which is not copyable by moveable. + class noncopyable_int : boost::noncopyable { + private: + int i; + public: + noncopyable_int(int x) : i(x) {} + noncopyable_int(const noncopyable_int&) = delete; + noncopyable_int& operator=(const noncopyable_int&) = delete; + noncopyable_int(noncopyable_int&& o) : i(o.i) {} + noncopyable_int& operator=(noncopyable_int&& o) { return o; } + bool operator!=(const noncopyable_int &rhs) { return i != rhs.i; } + friend std::ostream &operator<<(std::ostream &os, const noncopyable_int& x); + }; + + std::ostream & operator<<(std::ostream &os, const noncopyable_int& x) + { + return os << x.i; + } + + template< class Container > + void test_push_back_move_impl(std::size_t n) + { + Container test; + Container reference; + for (std::size_t i = 0; i < n; ++i) + reference.push_back(noncopyable_int(i)); + + { + Container to_push_back; + for (std::size_t i = 0; i < n; ++i) + to_push_back.push_back(noncopyable_int(i)); + + boost::push_back(test, std::move(to_push_back)); + + BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(), + test.begin(), test.end() ); + } + // Do it again to push onto non-empty container + for (std::size_t i = 0; i < n; ++i) + reference.push_back(noncopyable_int(i)); + + { + Container to_push_back; + for (std::size_t i = 0; i < n; ++i) + to_push_back.push_back(noncopyable_int(i)); + + boost::push_back(test, std::move(to_push_back)); + + BOOST_CHECK_EQUAL_COLLECTIONS( reference.begin(), reference.end(), + test.begin(), test.end() ); + } + } + + template< class Container > + void test_push_back_move_impl() + { + test_push_back_move_impl< Container >(0); + test_push_back_move_impl< Container >(1); + test_push_back_move_impl< Container >(2); + test_push_back_move_impl< Container >(100); + } + + void test_push_back_move() + { + test_push_back_move_impl< std::vector >(); + test_push_back_move_impl< std::list >(); + } +#endif } boost::unit_test::test_suite* @@ -67,6 +138,9 @@ init_unit_test_suite(int argc, char* argv[]) = BOOST_TEST_SUITE( "RangeTestSuite.algorithm_ext.push_back" ); test->add( BOOST_TEST_CASE( &test_push_back ) ); +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + test->add( BOOST_TEST_CASE( &test_push_back_move ) ); +#endif return test; }