Added more tests for move operations, fixed bugs, disabled optional<T&&>.

This commit is contained in:
Andrzej Krzemienski
2014-04-26 23:24:21 +02:00
parent 5c69bac12f
commit 0e61751fab
5 changed files with 170 additions and 11 deletions

View File

@ -21,13 +21,14 @@
#include <boost/config.hpp> #include <boost/config.hpp>
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/static_assert.hpp>
#include <boost/type.hpp> #include <boost/type.hpp>
#include <boost/type_traits/alignment_of.hpp> #include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/has_nothrow_constructor.hpp> #include <boost/type_traits/has_nothrow_constructor.hpp>
#include <boost/type_traits/type_with_alignment.hpp> #include <boost/type_traits/type_with_alignment.hpp>
#include <boost/type_traits/remove_reference.hpp> #include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/decay.hpp> #include <boost/type_traits/decay.hpp>
#include <boost/type_traits/is_base_of.hpp> #include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/is_reference.hpp> #include <boost/type_traits/is_reference.hpp>
#include <boost/type_traits/is_same.hpp> #include <boost/type_traits/is_same.hpp>
#include <boost/mpl/if.hpp> #include <boost/mpl/if.hpp>
@ -139,6 +140,7 @@ struct types_when_isnt_ref
typedef T & reference_type ; typedef T & reference_type ;
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
typedef T && rval_reference_type ; typedef T && rval_reference_type ;
static rval_reference_type move(reference_type r) { return boost::move(r); }
#endif #endif
typedef T const* pointer_const_type ; typedef T const* pointer_const_type ;
typedef T * pointer_type ; typedef T * pointer_type ;
@ -153,6 +155,7 @@ struct types_when_is_ref
typedef raw_type& reference_type ; typedef raw_type& reference_type ;
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
typedef raw_type&& rval_reference_type ; typedef raw_type&& rval_reference_type ;
static reference_type move(reference_type r) { return r; }
#endif #endif
typedef raw_type* pointer_const_type ; typedef raw_type* pointer_const_type ;
typedef raw_type* pointer_type ; typedef raw_type* pointer_type ;
@ -310,6 +313,24 @@ class optional_base : public optional_tag
construct(rhs.get_impl()); construct(rhs.get_impl());
} }
} }
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
// Assigns from another optional<T> (deep-moves the rhs value)
void assign ( optional_base&& rhs )
{
if (is_initialized())
{
if ( rhs.is_initialized() )
assign_value(boost::move(rhs.get_impl()), is_reference_predicate() );
else destroy();
}
else
{
if ( rhs.is_initialized() )
construct(boost::move(rhs.get_impl()));
}
}
#endif
// Assigns from another _convertible_ optional<U> (deep-copies the rhs value) // Assigns from another _convertible_ optional<U> (deep-copies the rhs value)
template<class U> template<class U>
@ -328,7 +349,7 @@ class optional_base : public optional_tag
} }
} }
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
// move-assigns from another _convertible_ optional<U> (deep-moves from the rhs value) // move-assigns from another _convertible_ optional<U> (deep-moves from the rhs value)
template<class U> template<class U>
void assign ( optional<U>&& rhs ) void assign ( optional<U>&& rhs )
@ -420,7 +441,7 @@ class optional_base : public optional_tag
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
void construct ( rval_reference_type val ) void construct ( rval_reference_type val )
{ {
new (m_storage.address()) internal_type( boost::move(val) ) ; new (m_storage.address()) internal_type( types::move(val) ) ;
m_initialized = true ; m_initialized = true ;
} }
#endif #endif
@ -726,7 +747,6 @@ class optional : public optional_detail::optional_base<T>
explicit optional ( Expr&& expr, explicit optional ( Expr&& expr,
typename boost::disable_if_c< typename boost::disable_if_c<
(boost::is_base_of<optional_detail::optional_tag, typename boost::decay<Expr>::type>::value) || (boost::is_base_of<optional_detail::optional_tag, typename boost::decay<Expr>::type>::value) ||
boost::is_same<typename boost::decay<Expr>::type, optional>::value ||
boost::is_same<typename boost::decay<Expr>::type, none_t>::value >::type* = 0 boost::is_same<typename boost::decay<Expr>::type, none_t>::value >::type* = 0
) )
: base(boost::forward<Expr>(expr),boost::addressof(expr)) {} : base(boost::forward<Expr>(expr),boost::addressof(expr)) {}
@ -760,7 +780,7 @@ class optional : public optional_detail::optional_base<T>
template<class Expr> template<class Expr>
typename boost::disable_if_c< typename boost::disable_if_c<
boost::is_base_of<optional_detail::optional_tag, typename boost::decay<Expr>::type>::value || boost::is_same<typename boost::decay<Expr>::type, optional>::value || boost::is_same<typename boost::decay<Expr>::type, none_t>::value, boost::is_base_of<optional_detail::optional_tag, typename boost::decay<Expr>::type>::value || boost::is_same<typename boost::decay<Expr>::type, none_t>::value,
optional& optional&
>::type >::type
operator= ( Expr&& expr ) operator= ( Expr&& expr )
@ -873,6 +893,14 @@ class optional : public optional_detail::optional_base<T>
bool operator!() const BOOST_NOEXCEPT { return !this->is_initialized() ; } bool operator!() const BOOST_NOEXCEPT { return !this->is_initialized() ; }
} ; } ;
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
template<class T>
class optional<T&&>
{
BOOST_STATIC_ASSERT_MSG(sizeof(T) == 0, "Optional rvalue references are illegal.");
} ;
#endif
// Returns optional<T>(v) // Returns optional<T>(v)
template<class T> template<class T>
inline inline

View File

@ -30,5 +30,7 @@ import testing ;
[ compile-fail optional_test_ref_fail4.cpp ] [ compile-fail optional_test_ref_fail4.cpp ]
[ compile-fail optional_test_inplace_fail.cpp ] [ compile-fail optional_test_inplace_fail.cpp ]
[ compile-fail optional_test_inplace_fail2.cpp ] [ compile-fail optional_test_inplace_fail2.cpp ]
[ compile-fail optional_test_fail_copying_a_moveable_type.cpp ]
[ compile-fail optional_test_fail_optional_rvalue_ref.cpp ]
; ;
} }

View File

@ -0,0 +1,36 @@
// Copyright (C) 2014, Andrzej Krzemienski.
//
// Use, modification, and distribution is subject to 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)
//
// See http://www.boost.org/lib/optional for documentation.
//
// You are welcome to contact the author at:
// akrzemi1@gmail.com
//
#include "boost/optional.hpp"
class MoveOnly
{
public:
int val;
MoveOnly(int v) : val(v) {}
MoveOnly(MoveOnly&& rhs) : val(rhs.val) { rhs.val = 0; }
void operator=(MoveOnly&& rhs) {val = rhs.val; rhs.val = 0; }
private:
MoveOnly(MoveOnly const&);
void operator=(MoveOnly const&);
};
//
// THIS TEST SHOULD FAIL TO COMPILE
//
void test_copying_optional_with_noncopyable_T()
{
boost::optional<MoveOnly> opt1 ;
boost::optional<MoveOnly> opt2(opt1) ;
}

View File

@ -0,0 +1,19 @@
// Copyright (C) 2014, Andrzej Krzemienski.
//
// Use, modification, and distribution is subject to 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)
//
// See http://www.boost.org/lib/optional for documentation.
//
// You are welcome to contact the author at:
// akrzemi1@gmail.com
//
#include "boost/optional.hpp"
//
// THIS TEST SHOULD FAIL TO COMPILE
//
boost::optional<int&&> oi;

View File

@ -20,6 +20,7 @@
#include "boost/bind/apply.hpp" // Included just to test proper interaction with boost::apply<> as reported by Daniel Wallin #include "boost/bind/apply.hpp" // Included just to test proper interaction with boost::apply<> as reported by Daniel Wallin
#include "boost/mpl/bool.hpp" #include "boost/mpl/bool.hpp"
#include "boost/mpl/bool_fwd.hpp" // For mpl::true_ and mpl::false_ #include "boost/mpl/bool_fwd.hpp" // For mpl::true_ and mpl::false_
#include "boost/static_assert.hpp"
#include "boost/optional/optional.hpp" #include "boost/optional/optional.hpp"
@ -91,7 +92,6 @@ void test_move_ctor_from_U()
OracleVal v1; OracleVal v1;
optional<Oracle> o2 (v1); optional<Oracle> o2 (v1);
BOOST_CHECK(o2); BOOST_CHECK(o2);
std::cout << "AK" << " @@@ " << o2->s << std::endl;
BOOST_CHECK(o2->s == sValueCopyConstructed || o2->s == sCopyConstructed || o2->s == sMoveConstructed ); BOOST_CHECK(o2->s == sValueCopyConstructed || o2->s == sCopyConstructed || o2->s == sMoveConstructed );
BOOST_CHECK(v1.s == sIntConstructed); BOOST_CHECK(v1.s == sIntConstructed);
@ -204,7 +204,7 @@ void test_move_assign_from_T()
BOOST_CHECK(v1.s == sMovedFrom); BOOST_CHECK(v1.s == sMovedFrom);
} }
void test_move_ssign_from_optional_T() void test_move_assign_from_optional_T()
{ {
optional<Oracle> o1; optional<Oracle> o1;
optional<Oracle> o2; optional<Oracle> o2;
@ -212,8 +212,8 @@ void test_move_ssign_from_optional_T()
BOOST_CHECK(!o1); BOOST_CHECK(!o1);
optional<Oracle> o3((Oracle())); optional<Oracle> o3((Oracle()));
o1 = o3; o1 = o3;
/*BOOST_CHECK(o3); BOOST_CHECK(o3);
BOOST_CHECK(o3->s == sDefaultConstructed); BOOST_CHECK(o3->s == sMoveConstructed);
BOOST_CHECK(o1); BOOST_CHECK(o1);
BOOST_CHECK(o1->s == sCopyConstructed); BOOST_CHECK(o1->s == sCopyConstructed);
@ -225,9 +225,82 @@ void test_move_ssign_from_optional_T()
o2 = optional<Oracle>((Oracle())); o2 = optional<Oracle>((Oracle()));
BOOST_CHECK(o2); BOOST_CHECK(o2);
BOOST_CHECK(o2->s == sMoveAssigned);*/ BOOST_CHECK(o2->s == sMoveAssigned);
} }
class MoveOnly
{
public:
int val;
MoveOnly(int v) : val(v) {}
MoveOnly(MoveOnly&& rhs) : val(rhs.val) { rhs.val = 0; }
void operator=(MoveOnly&& rhs) {val = rhs.val; rhs.val = 0; }
private:
MoveOnly(MoveOnly const&);
void operator=(MoveOnly const&);
};
void test_with_move_only()
{
optional<MoveOnly> o1;
optional<MoveOnly> o2((MoveOnly(1)));
BOOST_CHECK(o2);
BOOST_CHECK(o2->val == 1);
optional<MoveOnly> o3 (boost::move(o1));
BOOST_CHECK(!o3);
optional<MoveOnly> o4 (boost::move(o2));
BOOST_CHECK(o4);
BOOST_CHECK(o4->val == 1);
BOOST_CHECK(o2);
BOOST_CHECK(o2->val == 0);
o3 = boost::move(o4);
BOOST_CHECK(o3);
BOOST_CHECK(o3->val == 1);
BOOST_CHECK(o4);
BOOST_CHECK(o4->val == 0);
}
// these 4 classes have different noexcept signatures in move operations
struct NothrowBoth {
NothrowBoth(NothrowBoth&&) BOOST_NOEXCEPT_IF(true) {};
void operator=(NothrowBoth&&) BOOST_NOEXCEPT_IF(true) {};
};
struct NothrowCtor {
NothrowCtor(NothrowCtor&&) BOOST_NOEXCEPT_IF(true) {};
void operator=(NothrowCtor&&) BOOST_NOEXCEPT_IF(false) {};
};
struct NothrowAssign {
NothrowAssign(NothrowAssign&&) BOOST_NOEXCEPT_IF(false) {};
void operator=(NothrowAssign&&) BOOST_NOEXCEPT_IF(true) {};
};
struct NothrowNone {
NothrowNone(NothrowNone&&) BOOST_NOEXCEPT_IF(false) {};
void operator=(NothrowNone&&) BOOST_NOEXCEPT_IF(false) {};
};
#ifndef BOOST_NO_NOEXCEPT
void test_noexcept() // this is a compile-time test
{
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible<optional<NothrowBoth> >::value);
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_assignable<optional<NothrowBoth> >::value);
BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional<NothrowBoth>()));
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible<optional<NothrowCtor> >::value);
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable<optional<NothrowCtor> >::value);
BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional<NothrowCtor>()));
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible<optional<NothrowAssign> >::value);
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable<optional<NothrowAssign> >::value);
BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional<NothrowAssign>()));
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible<optional<NothrowNone> >::value);
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable<optional<NothrowNone> >::value);
BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional<NothrowNone>()));
}
#endif // !defned BOOST_NO_NOEXCEPT
#endif #endif
@ -241,7 +314,8 @@ int test_main( int, char* [] )
test_move_ctor_from_optional_T(); test_move_ctor_from_optional_T();
test_move_assign_from_U(); test_move_assign_from_U();
test_move_assign_from_T(); test_move_assign_from_T();
test_move_ssign_from_optional_T(); test_move_assign_from_optional_T();
test_with_move_only();
#endif #endif
} }
catch ( ... ) catch ( ... )