From 587ef8e9889d4f597890bc78a8debe889b0dd0d3 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Mon, 14 Apr 2014 23:44:34 +0200 Subject: [PATCH 1/7] Added 'raw' move semantics; no unit-tests --- include/boost/optional/optional.hpp | 137 ++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 9 deletions(-) diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 603facd..4ae36f0 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -131,6 +131,9 @@ struct types_when_isnt_ref { typedef T const& reference_const_type ; typedef T & reference_type ; +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + typedef T && rval_reference_type ; +#endif typedef T const* pointer_const_type ; typedef T * pointer_type ; typedef T const& argument_type ; @@ -140,11 +143,14 @@ struct types_when_is_ref { typedef BOOST_DEDUCED_TYPENAME remove_reference::type raw_type ; - typedef raw_type& reference_const_type ; - typedef raw_type& reference_type ; - typedef raw_type* pointer_const_type ; - typedef raw_type* pointer_type ; - typedef raw_type& argument_type ; + typedef raw_type& reference_const_type ; + typedef raw_type& reference_type ; +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + typedef raw_type&& rval_reference_type ; +#endif + typedef raw_type* pointer_const_type ; + typedef raw_type* pointer_type ; + typedef raw_type& argument_type ; } ; struct optional_tag {} ; @@ -184,6 +190,9 @@ class optional_base : public optional_tag typedef BOOST_DEDUCED_TYPENAME types::reference_type reference_type ; typedef BOOST_DEDUCED_TYPENAME types::reference_const_type reference_const_type ; +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + typedef BOOST_DEDUCED_TYPENAME types::rval_reference_type rval_reference_type ; +#endif typedef BOOST_DEDUCED_TYPENAME types::pointer_type pointer_type ; typedef BOOST_DEDUCED_TYPENAME types::pointer_const_type pointer_const_type ; typedef BOOST_DEDUCED_TYPENAME types::argument_type argument_type ; @@ -209,6 +218,17 @@ class optional_base : public optional_tag construct(val); } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // move-construct an optional initialized from an rvalue-ref to 'val'. + // Can throw if T::T(T&&) does + optional_base ( rval_reference_type val ) + : + m_initialized(false) + { + construct( static_cast(val) ); + } +#endif + // Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialzed optional. // Can throw if T::T(T const&) does optional_base ( bool cond, argument_type val ) @@ -229,6 +249,17 @@ class optional_base : public optional_tag construct(rhs.get_impl()); } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Creates a deep move of another optional + // Can throw if T::T(T&&) does + optional_base ( optional_base&& rhs ) + : + m_initialized(false) + { + if ( rhs.is_initialized() ) + construct( static_cast(rhs.get_impl()) ); + } +#endif // This is used for both converting and in-place constructions. // Derived classes use the 'tag' to select the appropriate @@ -279,6 +310,26 @@ class optional_base : public optional_tag } } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // move-assigns from another _convertible_ optional (deep-moves from the rhs value) + template + void assign ( optional&& rhs ) + { + typedef BOOST_DEDUCED_TYPENAME optional::rval_reference_type ref_type; + if (is_initialized()) + { + if ( rhs.is_initialized() ) + assign_value(static_cast(rhs.get()), is_reference_predicate() ); + else destroy(); + } + else + { + if ( rhs.is_initialized() ) + construct(static_cast(rhs.get())); + } + } +#endif + // Assigns from a T (deep-copies the rhs value) void assign ( argument_type val ) { @@ -286,6 +337,16 @@ class optional_base : public optional_tag assign_value(val, is_reference_predicate() ); else construct(val); } + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Assigns from a T (deep-moves the rhs value) + void assign ( rval_reference_type val ) + { + if (is_initialized()) + assign_value( static_cast(val), is_reference_predicate() ); + else construct( static_cast(val) ); + } +#endif // Assigns from "none", destroying the current value, if any, leaving this UNINITIALIZED // No-throw (assuming T::~T() doesn't) @@ -325,6 +386,14 @@ class optional_base : public optional_tag new (m_storage.address()) internal_type(val) ; m_initialized = true ; } + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + void construct ( rval_reference_type val ) + { + new (m_storage.address()) internal_type( static_cast(val) ) ; + m_initialized = true ; + } +#endif #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT // Constructs in-place using the given factory @@ -411,6 +480,10 @@ class optional_base : public optional_tag void assign_value ( argument_type val, is_not_reference_tag ) { get_impl() = val; } void assign_value ( argument_type val, is_reference_tag ) { construct(val); } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + void assign_value ( rval_reference_type val, is_not_reference_tag ) { get_impl() = static_cast(val); } + void assign_value ( rval_reference_type val, is_reference_tag ) { construct( static_cast(val) ); } +#endif void destroy() { @@ -488,22 +561,34 @@ class optional : public optional_detail::optional_base typedef BOOST_DEDUCED_TYPENAME base::value_type value_type ; typedef BOOST_DEDUCED_TYPENAME base::reference_type reference_type ; typedef BOOST_DEDUCED_TYPENAME base::reference_const_type reference_const_type ; +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + typedef BOOST_DEDUCED_TYPENAME base::rval_reference_type rval_reference_type ; +#endif typedef BOOST_DEDUCED_TYPENAME base::pointer_type pointer_type ; typedef BOOST_DEDUCED_TYPENAME base::pointer_const_type pointer_const_type ; typedef BOOST_DEDUCED_TYPENAME base::argument_type argument_type ; // Creates an optional uninitialized. // No-throw - optional() : base() {} + optional() BOOST_NOEXCEPT : base() {} // Creates an optional uninitialized. // No-throw - optional( none_t none_ ) : base(none_) {} + optional( none_t none_ ) BOOST_NOEXCEPT : base(none_) {} // Creates an optional initialized with 'val'. // Can throw if T::T(T const&) does optional ( argument_type val ) : base(val) {} +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Creates an optional initialized with 'move(val)'. + // Can throw if T::T(T &&) does + optional ( rval_reference_type val ) BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(static_cast(val)))) + : + base( static_cast(val) ) + {} +#endif + // Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialized optional. // Can throw if T::T(T const&) does optional ( bool cond, argument_type val ) : base(cond,val) {} @@ -521,6 +606,20 @@ class optional : public optional_detail::optional_base if ( rhs.is_initialized() ) this->construct(rhs.get()); } + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Creates a deep move of another convertible optional + // Requires a valid conversion from U to T. + // Can throw if T::T(U&&) does + template + explicit optional ( optional && rhs ) + : + base() + { + if ( rhs.is_initialized() ) + this->construct( static_cast::rval_reference_type>(rhs.get()) ); + } +#endif #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT // Creates an optional with an expression which can be either @@ -573,6 +672,16 @@ class optional : public optional_detail::optional_base return *this ; } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Assigns from another optional (deep-moves the rhs value) + optional& operator= ( optional && rhs ) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(static_cast(*rhs))) && BOOST_NOEXCEPT_EXPR(*rhs = static_cast(*rhs))) + { + this->assign( static_cast(rhs) ) ; + return *this ; + } +#endif + // Assigns from a T (deep-copies the rhs value) // Basic Guarantee: If T::( T const& ) throws, this is left UNINITIALIZED optional& operator= ( argument_type val ) @@ -581,6 +690,15 @@ class optional : public optional_detail::optional_base return *this ; } +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Assigns from a T (deep-moves the rhs value) + optional& operator= ( rval_reference_type val ) + { + this->assign( static_cast(val) ) ; + return *this ; + } +#endif + // Assigns from a "none" // Which destroys the current value, if any, leaving this UNINITIALIZED // No-throw (assuming T::~T() doesn't) @@ -591,10 +709,10 @@ class optional : public optional_detail::optional_base } void swap( optional & arg ) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(static_cast(*arg))) && BOOST_NOEXCEPT_EXPR(*arg = static_cast(*arg))) { // allow for Koenig lookup - using boost::swap; - swap(*this, arg); + boost::swap(*this, arg); } @@ -961,6 +1079,7 @@ template struct optional_swap_should_use_default_constructor : has_nothrow_default_constructor {} ; template inline void swap ( optional& x, optional& y ) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(static_cast(*x))) && BOOST_NOEXCEPT_EXPR(*y = static_cast(*x))) { optional_detail::swap_selector::value>::optional_swap(x, y); } From a26d11be8767f4e32974cb71fbd8aa071b19beaf Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Tue, 22 Apr 2014 22:36:19 +0200 Subject: [PATCH 2/7] Partially added move semantics (tests are still failing) --- include/boost/optional/optional.hpp | 157 ++++++++++++--- include/boost/optional/optional_fwd.hpp | 10 +- test/Jamfile.v2 | 25 +-- test/optional_test_move.cpp | 254 ++++++++++++++++++++++++ 4 files changed, 404 insertions(+), 42 deletions(-) create mode 100644 test/optional_test_move.cpp diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 4ae36f0..c063e94 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -36,6 +38,8 @@ #include #include #include +#include +#include #include @@ -91,7 +95,8 @@ class in_place_factory_base ; class typed_in_place_factory_base ; // This forward is needed to refer to namespace scope swap from the member swap -template void swap ( optional& x, optional& y ); +template void swap ( optional& x, optional& y ) + BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value); namespace optional_detail { @@ -225,7 +230,7 @@ class optional_base : public optional_tag : m_initialized(false) { - construct( static_cast(val) ); + construct( boost::move(val) ); } #endif @@ -257,10 +262,21 @@ class optional_base : public optional_tag m_initialized(false) { if ( rhs.is_initialized() ) - construct( static_cast(rhs.get_impl()) ); + construct( boost::move(rhs.get_impl()) ); } #endif +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + + template + explicit optional_base ( Expr&& expr, PtrExpr const* tag ) + : + m_initialized(false) + { + construct(boost::forward(expr),tag); + } + +#else // This is used for both converting and in-place constructions. // Derived classes use the 'tag' to select the appropriate // implementation (the correct 'construct()' overload) @@ -272,6 +288,7 @@ class optional_base : public optional_tag construct(expr,tag); } +#endif // No-throw (assuming T::~T() doesn't) @@ -338,13 +355,13 @@ class optional_base : public optional_tag else construct(val); } -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES // Assigns from a T (deep-moves the rhs value) void assign ( rval_reference_type val ) { if (is_initialized()) - assign_value( static_cast(val), is_reference_predicate() ); - else construct( static_cast(val) ); + assign_value( boost::move(val), is_reference_predicate() ); + else construct( boost::move(val) ); } #endif @@ -353,13 +370,25 @@ class optional_base : public optional_tag void assign ( none_t ) { destroy(); } #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + template + void assign_expr ( Expr&& expr, ExprPtr const* tag ) + { + if (is_initialized()) + assign_expr_to_initialized(boost::forward(expr),tag); + else construct(boost::forward(expr),tag); + } +#else template void assign_expr ( Expr const& expr, Expr const* tag ) - { - if (is_initialized()) - assign_expr_to_initialized(expr,tag); - else construct(expr,tag); - } + { + if (is_initialized()) + assign_expr_to_initialized(expr,tag); + else construct(expr,tag); + } +#endif + #endif public : @@ -390,7 +419,7 @@ class optional_base : public optional_tag #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES void construct ( rval_reference_type val ) { - new (m_storage.address()) internal_type( static_cast(val) ) ; + new (m_storage.address()) internal_type( boost::move(val) ) ; m_initialized = true ; } #endif @@ -430,7 +459,29 @@ class optional_base : public optional_tag } #endif - // Constructs using any expression implicitely convertible to the single argument +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Constructs using any expression implicitly convertible to the single argument + // of a one-argument T constructor. + // Converting constructions of optional from optional uses this function with + // 'Expr' being of type 'U' and relying on a converting constructor of T from U. + template + void construct ( Expr&& expr, void const* ) + { + new (m_storage.address()) internal_type(boost::forward(expr)) ; + m_initialized = true ; + } + + // Assigns using a form any expression implicitly convertible to the single argument + // of a T's assignment operator. + // Converting assignments of optional from optional uses this function with + // 'Expr' being of type 'U' and relying on a converting assignment of T from U. + template + void assign_expr_to_initialized ( Expr&& expr, void const* ) + { + assign_value(boost::forward(expr), is_reference_predicate()); + } +#else + // Constructs using any expression implicitly convertible to the single argument // of a one-argument T constructor. // Converting constructions of optional from optional uses this function with // 'Expr' being of type 'U' and relying on a converting constructor of T from U. @@ -441,7 +492,7 @@ class optional_base : public optional_tag m_initialized = true ; } - // Assigns using a form any expression implicitely convertible to the single argument + // Assigns using a form any expression implicitly convertible to the single argument // of a T's assignment operator. // Converting assignments of optional from optional uses this function with // 'Expr' being of type 'U' and relying on a converting assignment of T from U. @@ -451,6 +502,8 @@ class optional_base : public optional_tag assign_value(expr, is_reference_predicate()); } +#endif + #ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION // BCB5.64 (and probably lower versions) workaround. // The in-place factories are supported by means of catch-all constructors @@ -583,10 +636,7 @@ class optional : public optional_detail::optional_base #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES // Creates an optional initialized with 'move(val)'. // Can throw if T::T(T &&) does - optional ( rval_reference_type val ) BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(static_cast(val)))) - : - base( static_cast(val) ) - {} + optional ( rval_reference_type val ) : base( boost::forward(val) ) {} #endif // Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialized optional. @@ -617,7 +667,7 @@ class optional : public optional_detail::optional_base base() { if ( rhs.is_initialized() ) - this->construct( static_cast::rval_reference_type>(rhs.get()) ); + this->construct( boost::move(rhs.get()) ); } #endif @@ -625,32 +675,75 @@ class optional : public optional_detail::optional_base // Creates an optional with an expression which can be either // (a) An instance of InPlaceFactory (i.e. in_place(a,b,...,n); // (b) An instance of TypedInPlaceFactory ( i.e. in_place(a,b,...,n); - // (c) Any expression implicitely convertible to the single type + // (c) Any expression implicitly convertible to the single type // of a one-argument T's constructor. // (d*) Weak compilers (BCB) might also resolved Expr as optional and optional // even though explicit overloads are present for these. // Depending on the above some T ctor is called. - // Can throw is the resolved T ctor throws. + // Can throw if the resolved T ctor throws. +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + +#ifndef BOOST_NO_SFINAE +#else + template + explicit optional ( Expr&& expr, typename boost::disable_if::type, optional> >::type* = 0 ) + : base(boost::forward(expr),boost::addressof(expr)) {} +#endif +#else template explicit optional ( Expr const& expr ) : base(expr,boost::addressof(expr)) {} +#endif #endif // Creates a deep copy of another optional // Can throw if T::T(T const&) does optional ( optional const& rhs ) : base( static_cast(rhs) ) {} +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Creates a deep move of another optional + // Can throw if T::T(T&&) does + optional ( optional && rhs ) + BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value) + : base( boost::move(rhs) ) + {} + +#ifdef BOOST_NO_SFINAE + // To avoid too perfect forwarding + optional ( optional& rhs ) : base( static_cast(rhs) ) {} +#endif + +#endif // No-throw (assuming T::~T() doesn't) ~optional() {} #if !defined(BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT) && !defined(BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION) // Assigns from an expression. See corresponding constructor. // Basic Guarantee: If the resolved T ctor throws, this is left UNINITIALIZED +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + + template +#if !defined BOOST_NO_SFINAE + typename boost::disable_if< + boost::is_same::type, optional>, + optional& + >::type +#else + optional& +#endif + operator= ( Expr&& expr ) + { + this->assign_expr(boost::forward(expr),boost::addressof(expr)); + return *this ; + } + +#else template optional& operator= ( Expr const& expr ) { this->assign_expr(expr,boost::addressof(expr)); return *this ; } +#endif #endif // Assigns from another convertible optional (converts && deep-copies the rhs value) @@ -675,11 +768,19 @@ class optional : public optional_detail::optional_base #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES // Assigns from another optional (deep-moves the rhs value) optional& operator= ( optional && rhs ) - BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(static_cast(*rhs))) && BOOST_NOEXCEPT_EXPR(*rhs = static_cast(*rhs))) + BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value) { - this->assign( static_cast(rhs) ) ; + this->assign( static_cast(rhs) ) ; return *this ; } +#ifdef BOOST_NO_SFINAE + // to avoid too perfect forwarding + optional& operator= ( optional& rhs ) + { + this->assign( static_cast(rhs) ) ; + return *this ; + } +#endif #endif // Assigns from a T (deep-copies the rhs value) @@ -694,7 +795,7 @@ class optional : public optional_detail::optional_base // Assigns from a T (deep-moves the rhs value) optional& operator= ( rval_reference_type val ) { - this->assign( static_cast(val) ) ; + this->assign( boost::move(val) ) ; return *this ; } #endif @@ -709,7 +810,7 @@ class optional : public optional_detail::optional_base } void swap( optional & arg ) - BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(static_cast(*arg))) && BOOST_NOEXCEPT_EXPR(*arg = static_cast(*arg))) + BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value) { // allow for Koenig lookup boost::swap(*this, arg); @@ -740,11 +841,11 @@ class optional : public optional_detail::optional_base // implicit conversion to "bool" // No-throw - operator unspecified_bool_type() const { return this->safe_bool() ; } + operator unspecified_bool_type() const BOOST_NOEXCEPT { return this->safe_bool() ; } // This is provided for those compilers which don't like the conversion to bool // on some contexts. - bool operator!() const { return !this->is_initialized() ; } + bool operator!() const BOOST_NOEXCEPT { return !this->is_initialized() ; } } ; // Returns optional(v) @@ -1079,7 +1180,7 @@ template struct optional_swap_should_use_default_constructor : has_nothrow_default_constructor {} ; template inline void swap ( optional& x, optional& y ) - BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(static_cast(*x))) && BOOST_NOEXCEPT_EXPR(*y = static_cast(*x))) + BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value) { optional_detail::swap_selector::value>::optional_swap(x, y); } diff --git a/include/boost/optional/optional_fwd.hpp b/include/boost/optional/optional_fwd.hpp index 388cc1c..ade503d 100644 --- a/include/boost/optional/optional_fwd.hpp +++ b/include/boost/optional/optional_fwd.hpp @@ -11,15 +11,21 @@ // // Revisions: // 10 May 2008 (added swap related forward declaration) Niels Dekker -// +// 17 Apr 2014 (added noexcept) Andrzej Krzemienski +// #ifndef BOOST_OPTIONAL_OPTIONAL_FWD_FLC_19NOV2002_HPP #define BOOST_OPTIONAL_OPTIONAL_FWD_FLC_19NOV2002_HPP +#include +#include +#include + namespace boost { template class optional ; -template void swap ( optional& , optional& ) ; +template void swap ( optional& , optional& ) + BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value); template struct optional_swap_should_use_default_constructor ; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 16d4f3c..62ab883 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -17,17 +17,18 @@ import testing ; { test-suite optional : [ run optional_test.cpp ] - [ run optional_test_tie.cpp ] - [ run optional_test_ref.cpp ] - [ run optional_test_inplace.cpp ] - [ run optional_test_io.cpp ] - [ compile-fail optional_test_fail1.cpp ] - [ compile-fail optional_test_fail3a.cpp ] - [ compile-fail optional_test_fail3b.cpp ] - [ compile-fail optional_test_ref_fail1.cpp ] - [ compile-fail optional_test_ref_fail3.cpp ] - [ compile-fail optional_test_ref_fail4.cpp ] - [ compile-fail optional_test_inplace_fail.cpp ] - [ compile-fail optional_test_inplace_fail2.cpp ] + #[ run optional_test_tie.cpp ] + #[ run optional_test_ref.cpp ] + #[ run optional_test_inplace.cpp ] + #[ run optional_test_io.cpp ] + [ run optional_test_move.cpp ] + #[ compile-fail optional_test_fail1.cpp ] + #[ compile-fail optional_test_fail3a.cpp ] + #[ compile-fail optional_test_fail3b.cpp ] + #[ compile-fail optional_test_ref_fail1.cpp ] + #[ compile-fail optional_test_ref_fail3.cpp ] + #[ compile-fail optional_test_ref_fail4.cpp ] + #[ compile-fail optional_test_inplace_fail.cpp ] + #[ compile-fail optional_test_inplace_fail2.cpp ] ; } diff --git a/test/optional_test_move.cpp b/test/optional_test_move.cpp new file mode 100644 index 0000000..986ce43 --- /dev/null +++ b/test/optional_test_move.cpp @@ -0,0 +1,254 @@ +// 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 +// +// Revisions: +// +#include +#include +#include + +#define BOOST_ENABLE_ASSERT_HANDLER + +#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_fwd.hpp" // For mpl::true_ and mpl::false_ + +#include "boost/optional/optional.hpp" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#include "boost/none.hpp" + +#include "boost/test/minimal.hpp" + +#include "optional_test_common.cpp" + +//#ifndef BOOST_OPTIONAL_NO_CONVERTING_ASSIGNMENT +//#ifndef BOOST_OPTIONAL_NO_CONVERTING_COPY_CTOR + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + +enum State +{ + sDefaultConstructed, + sValueCopyConstructed, + sValueMoveConstructed, + sCopyConstructed, + sMoveConstructed, + sMoveAssigned, + sCopyAssigned, + sValueCopyAssigned, + sValueMoveAssigned, + sMovedFrom, + sIntConstructed +}; + +struct OracleVal +{ + State s; + int i; + OracleVal(int i = 0) : s(sIntConstructed), i(i) {} +}; + + +struct Oracle +{ + State s; + OracleVal val; + + Oracle() : s(sDefaultConstructed) {} + Oracle(const OracleVal& v) : s(sValueCopyConstructed), val(v) {} + Oracle(OracleVal&& v) : s(sValueMoveConstructed), val(std::move(v)) {v.s = sMovedFrom;} + Oracle(const Oracle& o) : s(sCopyConstructed), val(o.val) {} + Oracle(Oracle&& o) : s(sMoveConstructed), val(std::move(o.val)) {o.s = sMovedFrom;} + + Oracle& operator=(const OracleVal& v) { s = sValueCopyAssigned; val = v; return *this; } + Oracle& operator=(OracleVal&& v) { s = sValueMoveAssigned; val = std::move(v); v.s = sMovedFrom; return *this; } + Oracle& operator=(const Oracle& o) { s = sCopyAssigned; val = o.val; return *this; } + Oracle& operator=(Oracle&& o) { s = sMoveAssigned; val = std::move(o.val); o.s = sMovedFrom; return *this; } +}; + +bool operator==( Oracle const& a, Oracle const& b ) { return a.val.i == b.val.i; } +bool operator!=( Oracle const& a, Oracle const& b ) { return a.val.i != b.val.i; } + + +void test_move_ctor_from_U() +{ + optional o1 ((OracleVal())); + BOOST_CHECK(o1); + BOOST_CHECK(o1->s == sValueMoveConstructed || o1->s == sMoveConstructed); + + OracleVal v1; + optional o2 (v1); + BOOST_CHECK(o2); + std::cout << "AK" << " @@@ " << o2->s << std::endl; + BOOST_CHECK(o2->s == sValueCopyConstructed || o2->s == sCopyConstructed || o2->s == sMoveConstructed ); + BOOST_CHECK(v1.s == sIntConstructed); + + optional o3 (boost::move(v1)); + BOOST_CHECK(o3); + BOOST_CHECK(o3->s == sValueMoveConstructed || o3->s == sMoveConstructed); + BOOST_CHECK(v1.s == sMovedFrom); +} + +void test_move_ctor_form_T() +{ + optional o1 ((Oracle())); + BOOST_CHECK(o1); + BOOST_CHECK(o1->s == sMoveConstructed); + + Oracle v1; + optional o2 (v1); + BOOST_CHECK(o2); + BOOST_CHECK(o2->s == sCopyConstructed); + BOOST_CHECK(v1.s == sDefaultConstructed); + + optional o3 (boost::move(v1)); + BOOST_CHECK(o3); + BOOST_CHECK(o3->s == sMoveConstructed); + BOOST_CHECK(v1.s == sMovedFrom); +} + +void test_move_ctor_from_optional_T() +{ + optional o1; + optional o2(boost::move(o1)); + + BOOST_CHECK(!o1); + BOOST_CHECK(!o2); + + optional o3((Oracle())); + optional o4(boost::move(o3)); + BOOST_CHECK(o3); + BOOST_CHECK(o4); + BOOST_CHECK(o3->s == sMovedFrom); + BOOST_CHECK(o4->s == sMoveConstructed); + + optional o5((optional())); + BOOST_CHECK(!o5); + + optional o6((optional(Oracle()))); + BOOST_CHECK(o6); + BOOST_CHECK(o6->s == sMoveConstructed); + + optional o7(o6); // does copy ctor from non-const lvalue compile? +} + +void test_move_assign_from_U() +{ + optional o1; + o1 = OracleVal(); + BOOST_CHECK(o1); + + BOOST_CHECK(o1->s == sValueMoveConstructed); + + o1 = OracleVal(); + BOOST_CHECK(o1); + BOOST_CHECK(o1->s == sMoveAssigned); + + OracleVal v1; + optional o2; + o2 = v1; + BOOST_CHECK(o2); + BOOST_CHECK(o2->s == sValueCopyConstructed); + BOOST_CHECK(v1.s == sIntConstructed); + o2 = v1; + BOOST_CHECK(o2); + BOOST_CHECK(o2->s == sCopyAssigned || o2->s == sMoveAssigned); + BOOST_CHECK(v1.s == sIntConstructed); + + optional o3; + o3 = boost::move(v1); + BOOST_CHECK(o3); + BOOST_CHECK(o3->s == sValueMoveConstructed); + BOOST_CHECK(v1.s == sMovedFrom); +} + +void test_move_assign_from_T() +{ + optional o1; + o1 = Oracle(); + BOOST_CHECK(o1); + BOOST_CHECK(o1->s == sMoveConstructed); + + o1 = Oracle(); + BOOST_CHECK(o1); + BOOST_CHECK(o1->s == sMoveAssigned); + + Oracle v1; + optional o2; + o2 = v1; + BOOST_CHECK(o2); + BOOST_CHECK(o2->s == sCopyConstructed); + BOOST_CHECK(v1.s == sDefaultConstructed); + o2 = v1; + BOOST_CHECK(o2); + BOOST_CHECK(o2->s == sCopyAssigned); + BOOST_CHECK(v1.s == sDefaultConstructed); + + optional o3; + o3 = boost::move(v1); + BOOST_CHECK(o3); + BOOST_CHECK(o3->s == sMoveConstructed); + BOOST_CHECK(v1.s == sMovedFrom); +} + +void test_move_ssign_from_optional_T() +{ + optional o1; + optional o2; + o1 = optional(); + BOOST_CHECK(!o1); + optional o3((Oracle())); + o1 = o3; + /*BOOST_CHECK(o3); + BOOST_CHECK(o3->s == sDefaultConstructed); + BOOST_CHECK(o1); + BOOST_CHECK(o1->s == sCopyConstructed); + + o2 = boost::move(o3); + BOOST_CHECK(o3); + BOOST_CHECK(o3->s == sMovedFrom); + BOOST_CHECK(o2); + BOOST_CHECK(o2->s == sMoveConstructed); + + o2 = optional((Oracle())); + BOOST_CHECK(o2); + BOOST_CHECK(o2->s == sMoveAssigned);*/ +} + + +#endif + +int test_main( int, char* [] ) +{ + try + { +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + test_move_ctor_from_U(); + test_move_ctor_form_T(); + test_move_ctor_from_optional_T(); + test_move_assign_from_U(); + test_move_assign_from_T(); + test_move_ssign_from_optional_T(); +#endif + } + catch ( ... ) + { + BOOST_ERROR("Unexpected Exception caught!"); + } + + return 0; +} + + From 5c69bac12fd362242838b7c622c4552903808065 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sat, 26 Apr 2014 00:22:39 +0200 Subject: [PATCH 3/7] Fixed unit tests (still need to add more unit tests for move semantics) --- include/boost/optional/optional.hpp | 75 +++++++++++++++++++---------- test/Jamfile.v2 | 24 ++++----- test/optional_test_move.cpp | 3 +- 3 files changed, 64 insertions(+), 38 deletions(-) diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index c063e94..6933a05 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -27,6 +27,7 @@ #include #include #include + #include #include #include #include @@ -425,6 +426,41 @@ class optional_base : public optional_tag #endif #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Constructs in-place using the given factory + template + void construct ( Expr&& factory, in_place_factory_base const* ) + { + BOOST_STATIC_ASSERT ( ::boost::mpl::not_::value ) ; + boost_optional_detail::construct(factory, m_storage.address()); + m_initialized = true ; + } + + // Constructs in-place using the given typed factory + template + void construct ( Expr&& factory, typed_in_place_factory_base const* ) + { + BOOST_STATIC_ASSERT ( ::boost::mpl::not_::value ) ; + factory.apply(m_storage.address()) ; + m_initialized = true ; + } + + template + void assign_expr_to_initialized ( Expr&& factory, in_place_factory_base const* tag ) + { + destroy(); + construct(factory,tag); + } + + // Constructs in-place using the given typed factory + template + void assign_expr_to_initialized ( Expr&& factory, typed_in_place_factory_base const* tag ) + { + destroy(); + construct(factory,tag); + } +#else // Constructs in-place using the given factory template void construct ( Expr const& factory, in_place_factory_base const* ) @@ -459,6 +495,8 @@ class optional_base : public optional_tag } #endif +#endif + #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES // Constructs using any expression implicitly convertible to the single argument // of a one-argument T constructor. @@ -683,12 +721,16 @@ class optional : public optional_detail::optional_base // Can throw if the resolved T ctor throws. #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES -#ifndef BOOST_NO_SFINAE -#else + template - explicit optional ( Expr&& expr, typename boost::disable_if::type, optional> >::type* = 0 ) + explicit optional ( Expr&& expr, + typename boost::disable_if_c< + (boost::is_base_of::type>::value) || + boost::is_same::type, optional>::value || + boost::is_same::type, none_t>::value >::type* = 0 + ) : base(boost::forward(expr),boost::addressof(expr)) {} -#endif + #else template explicit optional ( Expr const& expr ) : base(expr,boost::addressof(expr)) {} @@ -707,11 +749,6 @@ class optional : public optional_detail::optional_base : base( boost::move(rhs) ) {} -#ifdef BOOST_NO_SFINAE - // To avoid too perfect forwarding - optional ( optional& rhs ) : base( static_cast(rhs) ) {} -#endif - #endif // No-throw (assuming T::~T() doesn't) ~optional() {} @@ -722,14 +759,10 @@ class optional : public optional_detail::optional_base #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template -#if !defined BOOST_NO_SFINAE - typename boost::disable_if< - boost::is_same::type, optional>, + typename boost::disable_if_c< + boost::is_base_of::type>::value || boost::is_same::type, optional>::value || boost::is_same::type, none_t>::value, optional& >::type -#else - optional& -#endif operator= ( Expr&& expr ) { this->assign_expr(boost::forward(expr),boost::addressof(expr)); @@ -743,8 +776,8 @@ class optional : public optional_detail::optional_base this->assign_expr(expr,boost::addressof(expr)); return *this ; } -#endif -#endif +#endif // !defined BOOST_NO_CXX11_RVALUE_REFERENCES +#endif // !defined(BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT) && !defined(BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION) // Assigns from another convertible optional (converts && deep-copies the rhs value) // Requires a valid conversion from U to T. @@ -773,14 +806,6 @@ class optional : public optional_detail::optional_base this->assign( static_cast(rhs) ) ; return *this ; } -#ifdef BOOST_NO_SFINAE - // to avoid too perfect forwarding - optional& operator= ( optional& rhs ) - { - this->assign( static_cast(rhs) ) ; - return *this ; - } -#endif #endif // Assigns from a T (deep-copies the rhs value) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 62ab883..a146f72 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -17,18 +17,18 @@ import testing ; { test-suite optional : [ run optional_test.cpp ] - #[ run optional_test_tie.cpp ] - #[ run optional_test_ref.cpp ] - #[ run optional_test_inplace.cpp ] - #[ run optional_test_io.cpp ] + [ run optional_test_tie.cpp ] + [ run optional_test_ref.cpp ] + [ run optional_test_inplace.cpp ] + [ run optional_test_io.cpp ] [ run optional_test_move.cpp ] - #[ compile-fail optional_test_fail1.cpp ] - #[ compile-fail optional_test_fail3a.cpp ] - #[ compile-fail optional_test_fail3b.cpp ] - #[ compile-fail optional_test_ref_fail1.cpp ] - #[ compile-fail optional_test_ref_fail3.cpp ] - #[ compile-fail optional_test_ref_fail4.cpp ] - #[ compile-fail optional_test_inplace_fail.cpp ] - #[ compile-fail optional_test_inplace_fail2.cpp ] + [ compile-fail optional_test_fail1.cpp ] + [ compile-fail optional_test_fail3a.cpp ] + [ compile-fail optional_test_fail3b.cpp ] + [ compile-fail optional_test_ref_fail1.cpp ] + [ compile-fail optional_test_ref_fail3.cpp ] + [ compile-fail optional_test_ref_fail4.cpp ] + [ compile-fail optional_test_inplace_fail.cpp ] + [ compile-fail optional_test_inplace_fail2.cpp ] ; } diff --git a/test/optional_test_move.cpp b/test/optional_test_move.cpp index 986ce43..0f52c2d 100644 --- a/test/optional_test_move.cpp +++ b/test/optional_test_move.cpp @@ -146,7 +146,8 @@ void test_move_ctor_from_optional_T() void test_move_assign_from_U() { - optional o1; + optional o1 = boost::none; // test if additional ctors didn't break it + o1 = boost::none; // test if additional assignments didn't break it o1 = OracleVal(); BOOST_CHECK(o1); From 0e61751fab82a177a6deab511e1fb5f150e12534 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sat, 26 Apr 2014 23:24:21 +0200 Subject: [PATCH 4/7] Added more tests for move operations, fixed bugs, disabled optional. --- include/boost/optional/optional.hpp | 38 ++++++-- test/Jamfile.v2 | 2 + ...onal_test_fail_copying_a_moveable_type.cpp | 36 ++++++++ ...optional_test_fail_optional_rvalue_ref.cpp | 19 ++++ test/optional_test_move.cpp | 86 +++++++++++++++++-- 5 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 test/optional_test_fail_copying_a_moveable_type.cpp create mode 100644 test/optional_test_fail_optional_rvalue_ref.cpp diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 6933a05..053486e 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -21,13 +21,14 @@ #include #include +#include #include #include #include #include #include #include - #include +#include #include #include #include @@ -139,6 +140,7 @@ struct types_when_isnt_ref typedef T & reference_type ; #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES typedef T && rval_reference_type ; + static rval_reference_type move(reference_type r) { return boost::move(r); } #endif typedef T const* pointer_const_type ; typedef T * pointer_type ; @@ -153,6 +155,7 @@ struct types_when_is_ref typedef raw_type& reference_type ; #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES typedef raw_type&& rval_reference_type ; + static reference_type move(reference_type r) { return r; } #endif typedef raw_type* pointer_const_type ; typedef raw_type* pointer_type ; @@ -310,6 +313,24 @@ class optional_base : public optional_tag construct(rhs.get_impl()); } } + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Assigns from another optional (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 (deep-copies the rhs value) template @@ -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 (deep-moves from the rhs value) template void assign ( optional&& rhs ) @@ -420,7 +441,7 @@ class optional_base : public optional_tag #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES 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 ; } #endif @@ -726,7 +747,6 @@ class optional : public optional_detail::optional_base explicit optional ( Expr&& expr, typename boost::disable_if_c< (boost::is_base_of::type>::value) || - boost::is_same::type, optional>::value || boost::is_same::type, none_t>::value >::type* = 0 ) : base(boost::forward(expr),boost::addressof(expr)) {} @@ -760,7 +780,7 @@ class optional : public optional_detail::optional_base template typename boost::disable_if_c< - boost::is_base_of::type>::value || boost::is_same::type, optional>::value || boost::is_same::type, none_t>::value, + boost::is_base_of::type>::value || boost::is_same::type, none_t>::value, optional& >::type operator= ( Expr&& expr ) @@ -873,6 +893,14 @@ class optional : public optional_detail::optional_base bool operator!() const BOOST_NOEXCEPT { return !this->is_initialized() ; } } ; +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +template +class optional +{ + BOOST_STATIC_ASSERT_MSG(sizeof(T) == 0, "Optional rvalue references are illegal."); +} ; +#endif + // Returns optional(v) template inline diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index a146f72..e7e53fa 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -30,5 +30,7 @@ import testing ; [ compile-fail optional_test_ref_fail4.cpp ] [ compile-fail optional_test_inplace_fail.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 ] ; } diff --git a/test/optional_test_fail_copying_a_moveable_type.cpp b/test/optional_test_fail_copying_a_moveable_type.cpp new file mode 100644 index 0000000..c1194cd --- /dev/null +++ b/test/optional_test_fail_copying_a_moveable_type.cpp @@ -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 opt1 ; + boost::optional opt2(opt1) ; +} + + diff --git a/test/optional_test_fail_optional_rvalue_ref.cpp b/test/optional_test_fail_optional_rvalue_ref.cpp new file mode 100644 index 0000000..5a1e039 --- /dev/null +++ b/test/optional_test_fail_optional_rvalue_ref.cpp @@ -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 oi; + + diff --git a/test/optional_test_move.cpp b/test/optional_test_move.cpp index 0f52c2d..cad5866 100644 --- a/test/optional_test_move.cpp +++ b/test/optional_test_move.cpp @@ -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/mpl/bool.hpp" #include "boost/mpl/bool_fwd.hpp" // For mpl::true_ and mpl::false_ +#include "boost/static_assert.hpp" #include "boost/optional/optional.hpp" @@ -91,7 +92,6 @@ void test_move_ctor_from_U() OracleVal v1; optional o2 (v1); BOOST_CHECK(o2); - std::cout << "AK" << " @@@ " << o2->s << std::endl; BOOST_CHECK(o2->s == sValueCopyConstructed || o2->s == sCopyConstructed || o2->s == sMoveConstructed ); BOOST_CHECK(v1.s == sIntConstructed); @@ -204,7 +204,7 @@ void test_move_assign_from_T() BOOST_CHECK(v1.s == sMovedFrom); } -void test_move_ssign_from_optional_T() +void test_move_assign_from_optional_T() { optional o1; optional o2; @@ -212,8 +212,8 @@ void test_move_ssign_from_optional_T() BOOST_CHECK(!o1); optional o3((Oracle())); o1 = o3; - /*BOOST_CHECK(o3); - BOOST_CHECK(o3->s == sDefaultConstructed); + BOOST_CHECK(o3); + BOOST_CHECK(o3->s == sMoveConstructed); BOOST_CHECK(o1); BOOST_CHECK(o1->s == sCopyConstructed); @@ -225,9 +225,82 @@ void test_move_ssign_from_optional_T() o2 = optional((Oracle())); 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 o1; + optional o2((MoveOnly(1))); + BOOST_CHECK(o2); + BOOST_CHECK(o2->val == 1); + optional o3 (boost::move(o1)); + BOOST_CHECK(!o3); + optional 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 >::value); + BOOST_STATIC_ASSERT(::boost::is_nothrow_move_assignable >::value); + BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); + + BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible >::value); + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable >::value); + BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); + + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible >::value); + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable >::value); + BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); + + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible >::value); + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable >::value); + BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); +} +#endif // !defned BOOST_NO_NOEXCEPT #endif @@ -241,7 +314,8 @@ int test_main( int, char* [] ) test_move_ctor_from_optional_T(); test_move_assign_from_U(); test_move_assign_from_T(); - test_move_ssign_from_optional_T(); + test_move_assign_from_optional_T(); + test_with_move_only(); #endif } catch ( ... ) From 381614364688b975329b9f4debb2bb0b131ce6fe Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Mon, 28 Apr 2014 15:48:55 +0200 Subject: [PATCH 5/7] Disabled assignment and construction from rvalue references in optional --- include/boost/optional/optional.hpp | 39 +++++++++++++------ test/Jamfile.v2 | 4 ++ ...onal_test_ref_fail_assign_from_Trefref.cpp | 26 +++++++++++++ ...onal_test_ref_fail_assign_from_Urefref.cpp | 26 +++++++++++++ ...tional_test_ref_fail_init_from_Trefref.cpp | 25 ++++++++++++ ...tional_test_ref_fail_init_from_Urefref.cpp | 25 ++++++++++++ 6 files changed, 133 insertions(+), 12 deletions(-) create mode 100644 test/optional_test_ref_fail_assign_from_Trefref.cpp create mode 100644 test/optional_test_ref_fail_assign_from_Urefref.cpp create mode 100644 test/optional_test_ref_fail_init_from_Trefref.cpp create mode 100644 test/optional_test_ref_fail_init_from_Urefref.cpp diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 053486e..a8ba5e6 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -29,7 +29,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -146,6 +148,7 @@ struct types_when_isnt_ref typedef T * pointer_type ; typedef T const& argument_type ; } ; + template struct types_when_is_ref { @@ -162,6 +165,14 @@ struct types_when_is_ref typedef raw_type& argument_type ; } ; +template +void prevent_binding_rvalue_ref_to_optional_lvalue_ref() +{ + BOOST_STATIC_ASSERT_MSG( + !boost::is_lvalue_reference::value || !boost::is_rvalue_reference::value, + "binding rvalue references to optional lvalue references is disallowed"); +} + struct optional_tag {} ; template @@ -272,7 +283,7 @@ class optional_base : public optional_tag #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES - template + template explicit optional_base ( Expr&& expr, PtrExpr const* tag ) : m_initialized(false) @@ -695,7 +706,8 @@ class optional : public optional_detail::optional_base #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES // Creates an optional initialized with 'move(val)'. // Can throw if T::T(T &&) does - optional ( rval_reference_type val ) : base( boost::forward(val) ) {} + optional ( rval_reference_type val ) : base( boost::forward(val) ) + {optional_detail::prevent_binding_rvalue_ref_to_optional_lvalue_ref();} #endif // Creates an optional initialized with 'val' IFF cond is true, otherwise creates an uninitialized optional. @@ -740,22 +752,23 @@ class optional : public optional_detail::optional_base // even though explicit overloads are present for these. // Depending on the above some T ctor is called. // Can throw if the resolved T ctor throws. -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template explicit optional ( Expr&& expr, - typename boost::disable_if_c< - (boost::is_base_of::type>::value) || - boost::is_same::type, none_t>::value >::type* = 0 + BOOST_DEDUCED_TYPENAME boost::disable_if_c< + (boost::is_base_of::type>::value) || + boost::is_same::type, none_t>::value >::type* = 0 ) - : base(boost::forward(expr),boost::addressof(expr)) {} + : base(boost::forward(expr),boost::addressof(expr)) + {optional_detail::prevent_binding_rvalue_ref_to_optional_lvalue_ref();} #else template explicit optional ( Expr const& expr ) : base(expr,boost::addressof(expr)) {} -#endif -#endif +#endif // !defined BOOST_NO_CXX11_RVALUE_REFERENCES +#endif // !defined BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT // Creates a deep copy of another optional // Can throw if T::T(T const&) does @@ -779,12 +792,14 @@ class optional : public optional_detail::optional_base #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template - typename boost::disable_if_c< - boost::is_base_of::type>::value || boost::is_same::type, none_t>::value, + BOOST_DEDUCED_TYPENAME boost::disable_if_c< + boost::is_base_of::type>::value || + boost::is_same::type, none_t>::value, optional& >::type operator= ( Expr&& expr ) { + optional_detail::prevent_binding_rvalue_ref_to_optional_lvalue_ref(); this->assign_expr(boost::forward(expr),boost::addressof(expr)); return *this ; } @@ -836,7 +851,7 @@ class optional : public optional_detail::optional_base return *this ; } -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES // Assigns from a T (deep-moves the rhs value) optional& operator= ( rval_reference_type val ) { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e7e53fa..ac8017e 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -32,5 +32,9 @@ import testing ; [ 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 ] + [ compile-fail optional_test_ref_fail_init_from_Trefref.cpp ] + [ compile-fail optional_test_ref_fail_init_from_Urefref.cpp ] + [ compile-fail optional_test_ref_fail_assign_from_Trefref.cpp ] + [ compile-fail optional_test_ref_fail_assign_from_Urefref.cpp ] ; } diff --git a/test/optional_test_ref_fail_assign_from_Trefref.cpp b/test/optional_test_ref_fail_assign_from_Trefref.cpp new file mode 100644 index 0000000..4eb748a --- /dev/null +++ b/test/optional_test_ref_fail_assign_from_Trefref.cpp @@ -0,0 +1,26 @@ +// 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" + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +// +// THIS TEST SHOULD FAIL TO COMPILE +// +void optional_reference__test_no_assign_from_Trefref() +{ + boost::optional opt; + opt = int(3); +} + +#else +# error "Test skipped. This cannot be implemented w/o rvalue references." +#endif diff --git a/test/optional_test_ref_fail_assign_from_Urefref.cpp b/test/optional_test_ref_fail_assign_from_Urefref.cpp new file mode 100644 index 0000000..b06185f --- /dev/null +++ b/test/optional_test_ref_fail_assign_from_Urefref.cpp @@ -0,0 +1,26 @@ +// 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" + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +// +// THIS TEST SHOULD FAIL TO COMPILE +// +void optional_reference__test_no_assign_from_Urefref() +{ + boost::optional opt; + opt = long(3); +} + +#else +# error "Test skipped. This cannot be implemented w/o rvalue references." +#endif diff --git a/test/optional_test_ref_fail_init_from_Trefref.cpp b/test/optional_test_ref_fail_init_from_Trefref.cpp new file mode 100644 index 0000000..05752b8 --- /dev/null +++ b/test/optional_test_ref_fail_init_from_Trefref.cpp @@ -0,0 +1,25 @@ +// 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" + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +// +// THIS TEST SHOULD FAIL TO COMPILE +// +void optional_reference__test_no_init_from_Trefref() +{ + boost::optional opt = int(3); +} + +#else +# error "Test skipped. This cannot be implemented w/o rvalue references." +#endif diff --git a/test/optional_test_ref_fail_init_from_Urefref.cpp b/test/optional_test_ref_fail_init_from_Urefref.cpp new file mode 100644 index 0000000..e15192b --- /dev/null +++ b/test/optional_test_ref_fail_init_from_Urefref.cpp @@ -0,0 +1,25 @@ +// 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" + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +// +// THIS TEST SHOULD FAIL TO COMPILE +// +void optional_reference__test_no_init_from_Urefref() +{ + boost::optional opt = long(3); +} + +#else +# error "Test skipped. This cannot be implemented w/o rvalue references." +#endif From 01b22a0ff0f0c68dee34989555fdf857bf1ef2ca Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Mon, 28 Apr 2014 16:51:49 +0200 Subject: [PATCH 6/7] Added tests for move conversion between optional and optional --- include/boost/optional/optional.hpp | 29 +++++++++++++++- test/optional_test_move.cpp | 54 +++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index a8ba5e6..bbbe3be 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -587,6 +587,20 @@ class optional_base : public optional_tag // For VC<=70 compilers this workaround dosen't work becasue the comnpiler issues and error // instead of choosing the wrong overload // +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Notice that 'Expr' will be optional or optional (but not optional_base<..>) + template + void construct ( Expr&& expr, optional_tag const* ) + { + if ( expr.is_initialized() ) + { + // An exception can be thrown here. + // It it happens, THIS will be left uninitialized. + new (m_storage.address()) internal_type(types::move(expr.get())) ; + m_initialized = true ; + } + } +#else // Notice that 'Expr' will be optional or optional (but not optional_base<..>) template void construct ( Expr const& expr, optional_tag const* ) @@ -600,6 +614,7 @@ class optional_base : public optional_tag } } #endif +#endif // defined BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION void assign_value ( argument_type val, is_not_reference_tag ) { get_impl() = val; } void assign_value ( argument_type val, is_reference_tag ) { construct(val); } @@ -814,7 +829,7 @@ class optional : public optional_detail::optional_base #endif // !defined BOOST_NO_CXX11_RVALUE_REFERENCES #endif // !defined(BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT) && !defined(BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION) - // Assigns from another convertible optional (converts && deep-copies the rhs value) + // Copy-assigns from another convertible optional (converts && deep-copies the rhs value) // Requires a valid conversion from U to T. // Basic Guarantee: If T::T( U const& ) throws, this is left UNINITIALIZED template @@ -823,6 +838,18 @@ class optional : public optional_detail::optional_base this->assign(rhs); return *this ; } + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Move-assigns from another convertible optional (converts && deep-moves the rhs value) + // Requires a valid conversion from U to T. + // Basic Guarantee: If T::T( U && ) throws, this is left UNINITIALIZED + template + optional& operator= ( optional && rhs ) + { + this->assign(boost::move(rhs)); + return *this ; + } +#endif // Assigns from another optional (deep-copies the rhs value) // Basic Guarantee: If T::T( T const& ) throws, this is left UNINITIALIZED diff --git a/test/optional_test_move.cpp b/test/optional_test_move.cpp index cad5866..a2d20c1 100644 --- a/test/optional_test_move.cpp +++ b/test/optional_test_move.cpp @@ -239,6 +239,8 @@ public: private: MoveOnly(MoveOnly const&); void operator=(MoveOnly const&); + + friend class MoveOnlyB; }; void test_with_move_only() @@ -262,6 +264,56 @@ void test_with_move_only() BOOST_CHECK(o4->val == 0); } +class MoveOnlyB +{ +public: + int val; + MoveOnlyB(int v) : val(v) {} + MoveOnlyB(MoveOnlyB&& rhs) : val(rhs.val) { rhs.val = 0; } + void operator=(MoveOnlyB&& rhs) {val = rhs.val; rhs.val = 0; } + MoveOnlyB(MoveOnly&& rhs) : val(rhs.val) { rhs.val = 0; } + void operator=(MoveOnly&& rhs) {val = rhs.val; rhs.val = 0; } + +private: + MoveOnlyB(MoveOnlyB const&); + void operator=(MoveOnlyB const&); + MoveOnlyB(MoveOnly const&); + void operator=(MoveOnly const&); +}; + +void test_move_assign_from_optional_U() +{ + optional a((MoveOnly(2))); + optional b1; + b1 = boost::move(a); + + BOOST_CHECK(b1); + BOOST_CHECK(b1->val == 2); + BOOST_CHECK(a); + BOOST_CHECK(a->val == 0); + + b1 = MoveOnly(4); + + BOOST_CHECK(b1); + BOOST_CHECK(b1->val == 4); +} + +void test_move_ctor_from_optional_U() +{ + optional a((MoveOnly(2))); + optional b1(boost::move(a)); + + BOOST_CHECK(b1); + BOOST_CHECK(b1->val == 2); + BOOST_CHECK(a); + BOOST_CHECK(a->val == 0); + + optional b2(( optional(( MoveOnly(4) )) )); + + BOOST_CHECK(b2); + BOOST_CHECK(b2->val == 4); +} + // these 4 classes have different noexcept signatures in move operations struct NothrowBoth { NothrowBoth(NothrowBoth&&) BOOST_NOEXCEPT_IF(true) {}; @@ -312,9 +364,11 @@ int test_main( int, char* [] ) test_move_ctor_from_U(); test_move_ctor_form_T(); test_move_ctor_from_optional_T(); + test_move_ctor_from_optional_U(); test_move_assign_from_U(); test_move_assign_from_T(); test_move_assign_from_optional_T(); + test_move_assign_from_optional_U(); test_with_move_only(); #endif } From c9f1422560485545e91e1f7efa32775dcb1a7506 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Tue, 29 Apr 2014 01:06:14 +0200 Subject: [PATCH 7/7] Updated documentation; fixed optional::swap --- .../boost_optional/detailed_semantics.html | 436 +++++++++++++++++- .../boost_optional/optional_references.html | 16 + doc/html/boost_optional/synopsis.html | 18 +- doc/html/index.html | 2 +- doc/reference.qbk | 265 ++++++++++- doc/special_cases.qbk | 7 + include/boost/optional/optional.hpp | 39 +- include/boost/optional/optional_fwd.hpp | 5 +- test/optional_test_move.cpp | 31 ++ 9 files changed, 767 insertions(+), 52 deletions(-) diff --git a/doc/html/boost_optional/detailed_semantics.html b/doc/html/boost_optional/detailed_semantics.html index 4ab1a29..6fc4760 100644 --- a/doc/html/boost_optional/detailed_semantics.html +++ b/doc/html/boost_optional/detailed_semantics.html @@ -72,7 +72,7 @@ space

- optional<T>::optional(); + optional<T>::optional() noexcept;

  • @@ -82,11 +82,7 @@ Postconditions: *this is uninitialized.
  • - Throws: Nothing. -
  • -
  • - Notes: T's default constructor is not - called. + Notes: T's default constructor is not called.
  • Example: @@ -99,7 +95,7 @@ space

    - optional<T>::optional( none_t ); + optional<T>::optional( none_t ) noexcept;

    • @@ -109,9 +105,6 @@
    • Postconditions: *this is uninitialized.
    • -
    • - Throws: Nothing. -
    • Notes: T's default constructor is not called. @@ -132,12 +125,16 @@ optional<T (not a ref)>::optional( T const& v )

      +
    • + Requires: is_copy_constructible<T>::value + is true. +
    • Effect: Directly-Constructs an optional.
    • Postconditions: *this is initialized - and its value is acopy of v. + and its value is a copy of v.
    • Throws: Whatever T::T( @@ -193,6 +190,58 @@
    +

    + space +

    +

    + optional<T (not a ref)>::optional( T&& + v ) +

    +
      +
    • + Requires: is_move_constructible<T>::value + is true. +
    • +
    • + Effect: Directly-Move-Constructs an optional. +
    • +
    • + Postconditions: *this is initialized + and its value is move-constructed from v. +
    • +
    • + Throws: Whatever T::T( + T&& + ) throws. +
    • +
    • + Notes: T::T( + T&& + ) is called. +
    • +
    • + Exception Safety: Exceptions can only + be thrown during T::T( T&& ); + in that case, the state of v + is determined by exception safety guarantees for T::T(T&&). +
    • +
    • + Example: +
      T v1, v2;
      +optional<T> opt(std::move(v1));
      +assert ( *opt == v2 ) ;
      +
      +
    • +
    +

    + space +

    +

    + optional<T&>::optional( T&& ref ) = delete +

    +
    • + Notes: This constructor is deleted +

    space

    @@ -229,6 +278,10 @@ optional<T (not a ref)>::optional( optional const& rhs );

    +
  • + Requires: is_copy_constructible<T>::value + is true. +
  • Effect: Copy-Constructs an optional.
  • @@ -320,6 +373,116 @@
+

+ space +

+

+ optional<T (not a ref)>::optional( optional&& rhs + ) noexcept(see below); +

+
    +
  • + Requires: is_move_constructible<T>::value + is true. +
  • +
  • + Effect: Move-constructs an optional. +
  • +
  • + Postconditions: If rhs + is initialized, *this + is initialized and its value is move constructed from rhs; + else *this + is uninitialized. +
  • +
  • + Throws: Whatever T::T( + T&& + ) throws. +
  • +
  • + Notes: If rhs + is initialized, T::T( T + && ) + is called. The expression inside noexcept + is equivalent to is_nothrow_move_constructible<T>::value. +
  • +
  • + Exception Safety: Exceptions can only + be thrown during T::T( T&& ); + in that case, rhs remains + initialized and the value of *rhs is determined by exception safety + of T::T(T&&). +
  • +
  • + Example: +
    optional<std::unique_ptr<T>> uninit ;
    +assert (!uninit);
    +
    +optional<std::unique_ptr<T>> uinit2 ( std::move(uninit) ) ;
    +assert ( uninit2 == uninit );
    +
    +optional<std::unique_ptr<T>> init( std::uniqye_ptr<T>(new T(2)) );
    +assert ( **init == T(2) ) ;
    +
    +optional<std::unique_ptr<T>> init2 ( std::move(init) ) ;
    +assert ( init );
    +assert ( *init == nullptr );
    +assert ( init2 );
    +assert ( **init2 == T(2) ) ;
    +
    +
  • +
+

+ space +

+

+ optional<T&>::optional( optional && + rhs ); +

+
    +
  • + Effect: Move-Constructs an optional. +
  • +
  • + Postconditions: If rhs + is initialized, *this + is initialized and its value is another reference to the same object referenced + by *rhs; + else *this + is uninitialized. +
  • +
  • + Throws: Nothing. +
  • +
  • + Notes: If rhs + is initialized, both *this + and *rhs + will reefer to the same object (they alias). +
  • +
  • + Example: +
    optional<std::unique_ptr<T>&> uninit ;
    +assert (!uninit);
    +
    +optional<std::unique_ptr<T>&> uinit2 ( std::move(uninit) ) ;
    +assert ( uninit2 == uninit );
    +
    +std::unique_ptr<T> v(new T(2)) ;
    +optional<std::unique_ptr<T>&> init(v);
    +assert ( *init == v ) ;
    +
    +optional<std::unique_ptr<T>&> init2 ( std::move(init) ) ;
    +assert ( *init2 == v ) ;
    +
    +*v = 3 ;
    +
    +assert ( **init  == 3 ) ;
    +assert ( **init2 == 3 ) ;
    +
    +
  • +

space

@@ -368,6 +531,52 @@ +

+ space +

+

+ template<U> explicit optional<T + (not a ref)>::optional( optional<U>&& + rhs ); +

+
    +
  • + Effect: Move-constructs an optional. +
  • +
  • + Postconditions: If rhs + is initialized, *this + is initialized and its value is move constructed from *rhs; else *this is uninitialized. +
  • +
  • + Throws: Whatever T::T( + U&& + ) throws. +
  • +
  • + Notes: T::T( + U&& + ) is called if rhs + is initialized, which requires a valid conversion from U + to T. +
  • +
  • + Exception Safety: Exceptions can only + be thrown during T::T( U&& ); + in that case, rhs remains + initialized and the value of *rhs is determined by exception safety + guarantee of T::T( U&& ). +
  • +
  • + Example: +
    optional<double> x(123.4);
    +assert ( *x == 123.4 ) ;
    +
    +optional<int> y(std::move(x)) ;
    +assert( *y == 123 ) ;
    +
    +
  • +

space

@@ -511,6 +720,66 @@ +

+ space +

+

+ optional& + optional<T (not a ref)>::operator= ( T&& rhs + ) ; +

+
    +
  • + Effect: Moves the value rhs to an optional. +
  • +
  • + Postconditions: *this is initialized and its value is moved + from rhs. +
  • +
  • + Throws: Whatever T::operator=( T&& ) + or T::T(T + &&) throws. +
  • +
  • + Notes: If *this was initialized, T's + move-assignment operator is used, otherwise, its move-constructor is used. +
  • +
  • + Exception Safety: In the event of an exception, + the initialization state of *this is unchanged and its value unspecified + as far as optional is concerned + (it is up to T's operator=()). + If *this + is initially uninitialized and T's + move constructor fails, *this is left properly uninitialized. +
  • +
  • + Example: +
    T x;
    +optional<T> def ;
    +optional<T> opt(x) ;
    +
    +T y1, y2, yR;
    +def = std::move(y1) ;
    +assert ( *def == yR ) ;
    +opt = std::move(y2) ;
    +assert ( *opt == yR ) ;
    +
    +
  • +
+

+ space +

+

+ optional<T&>& + optional<T&>::operator= ( T&& + rhs ) + = delete; +

+
  • + Notes: This assignment operator is deleted. +

space

@@ -613,6 +882,75 @@ +

+ space +

+

+ optional& + optional<T (not a ref)>::operator= ( optional&& rhs + ) noexcept(see below); +

+
    +
  • + Effect: Move-assigns another optional to an optional. +
  • +
  • + Postconditions: If rhs + is initialized, *this + is initialized and its value is moved from *rhs, rhs + remains initialized; else *this is uninitialized. +
  • +
  • + Throws: Whatever T::operator( T&& + ) or T::T( + T && + ) throws. +
  • +
  • + Notes: If both *this and rhs + are initially initialized, T's + move assignment operator is used. If *this is + initially initialized but rhs + is uninitialized, T's [destructor] + is called. If *this + is initially uninitialized but rhs + is initialized, T's move + constructor is called. The expression inside noexcept + is equivalent to is_nothrow_move_constructible<T>::value + && is_nothrow_move_assignable<T>::value. +
  • +
  • + Exception Safety: In the event of an exception, + the initialization state of *this is unchanged and its value unspecified + as far as optional is concerned (it is up to T's + operator=()). + If *this + is initially uninitialized and T's + move constructor fails, *this is left properly uninitialized. +
  • +
  • + Example: +
    optional<T> opt(T(2)) ;
    +optional<T> def ;
    +
    +opt = def ;
    +assert ( def ) ;
    +assert ( opt ) ;
    +assert ( *opt == T(2) ) ;
    +
    +
  • +
+

+ space +

+

+ optional<T&> & optional<T&>::operator= ( optional<T&>&& + rhs ) + ; +

+
  • + Effect: Same as optional<T&>::operator= ( optional<T&> const& rhs ). +

space

@@ -673,6 +1011,59 @@ +

+ space +

+

+ template<U> optional& + optional<T (not a ref)>::operator= ( optional<U>&& + rhs ) + ; +

+
    +
  • + Effect: Move-assigns another convertible + optional to an optional. +
  • +
  • + Postconditions: If rhs + is initialized, *this + is initialized and its value is moved from the value of rhs; + else *this + is uninitialized. +
  • +
  • + Throws: Whatever T::operator=( U&& ) + or T::T( U&& ) + throws. +
  • +
  • + Notes: If both *this and rhs + are initially initialized, T's + assignment operator (from U&&) is used. If *this is initially initialized but rhs is uninitialized, T's + destructor is called. If *this is initially uninitialized but rhs is initialized, T's + converting constructor (from U&&) is called. +
  • +
  • + Exception Safety: In the event of an exception, + the initialization state of *this is unchanged and its value unspecified + as far as optional is concerned (it is up to T's + operator=()). + If *this + is initially uninitialized and T's + converting constructor fails, *this is left properly uninitialized. +
  • +
  • + Example: +
    T v;
    +optional<T> opt0(v);
    +optional<U> opt1;
    +
    +opt1 = std::move(opt0) ;
    +assert ( *opt1 == static_cast<U>(v) ) ;
    +
    +
  • +

space

@@ -768,7 +1159,7 @@

  • - Requirements: *this is initialized + Requires: *this is initialized
  • Returns: A reference to the contained @@ -870,7 +1261,7 @@

  • - Requirements: *this is initialized + Requires: *this is initialized
  • Returns: The @@ -956,7 +1347,7 @@

  • - Requirements: *this is initialized. + Requires: *this is initialized.
  • Returns: A pointer to the contained value. @@ -1005,16 +1396,13 @@ space

    - bool optional<T>::operator!() ; + bool optional<T>::operator!() noexcept ;

    • Returns: If *this is uninitialized, true; else false.
    • -
    • - Throws: Nothing. -
    • Notes: This operator is provided for those compilers which can't use the unspecified-bool-type operator @@ -1288,7 +1676,7 @@

      void swap ( optional<T>& - x, optional<T>& y ); + x, optional<T>& y ) ;

      • @@ -1305,19 +1693,21 @@
      • Throws: If both are initialized, whatever swap(T&,T&) - throws. If only one is initialized, whatever T::T ( T const& ) throws. + throws. If only one is initialized, whatever T::T ( T&& + ) throws.
      • Notes: If both are initialized, swap(T&,T&) is used unqualified but with std::swap introduced in scope. If only one is initialized, T::~T() and T::T( - T const& ) - is called. + T&& + ) is called.
      • Exception Safety: If both are initialized, this operation has the exception safety guarantees of swap(T&,T&). - If only one is initialized, it has the same basic guarantee as optional<T>::reset( T const& ). + If only one is initialized, it has the same basic guarantee as optional<T>::operator= ( T&& + ).
      • Example: diff --git a/doc/html/boost_optional/optional_references.html b/doc/html/boost_optional/optional_references.html index a603c1c..92770cf 100644 --- a/doc/html/boost_optional/optional_references.html +++ b/doc/html/boost_optional/optional_references.html @@ -68,6 +68,22 @@ than the reference itself.
      +

      + + Rvalue + references +

      +

      + Rvalue references and lvalue references to const have the ability in C++ to + extend the life time of a temporary they bind to. Optional references do not + have this capability, therefore to avoid surprising effects it is not possible + to initialize an optional references from a temporary. Optional rvalue references + are disabled altogether. Also, the initialization and assignment of an optional + reference to const from rvalue reference is disabled. +

      +
      const int& i = 1;            // legal
      +optional<const int&> oi = 1; // illegal
      +
    diff --git a/doc/html/boost_optional/synopsis.html b/doc/html/boost_optional/synopsis.html index 078dd02..1da981f 100644 --- a/doc/html/boost_optional/synopsis.html +++ b/doc/html/boost_optional/synopsis.html @@ -37,19 +37,25 @@ // (If T is of reference type, the parameters and results by reference are by value) - optional();R + optional()noexcept;R - optional(none_t);R + optional(none_t)noexcept;Roptional(Tconst&v);R + optional(T&&v);R + // [new in 1.34]optional(boolcondition,Tconst&v);Roptional(optionalconst&rhs);R + optional(optional&&rhs)noexcept(see below);R + template<classU>explicitoptional(optional<U>const&rhs);R + template<classU>explicitoptional(optional<U>&&rhs);R + template<classInPlaceFactory>explicitoptional(InPlaceFactoryconst&f);Rtemplate<classTypedInPlaceFactory>explicitoptional(TypedInPlaceFactoryconst&f);R @@ -58,10 +64,16 @@ optional&operator=(Tconst&v);R + optional&operator=(T&&v);R + optional&operator=(optionalconst&rhs);R + optional&operator=(optional&&rhs)noexcept(see below);R + template<classU>optional&operator=(optional<U>const&rhs);R + template<classU>optional&operator=(optional<U>&&rhs);R + template<classInPlaceFactory>optional&operator=(InPlaceFactoryconst&f);Rtemplate<classTypedInPlaceFactory>optional&operator=(TypedInPlaceFactoryconst&f);R @@ -83,7 +95,7 @@ operatorunspecified-bool-type()const;R - booloperator!()const;R + booloperator!()constnoexcept;R// deprecated methods diff --git a/doc/html/index.html b/doc/html/index.html index 2103f59..e1230c1 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -163,7 +163,7 @@
    - +

    Last revised: April 11, 2014 at 13:25:26 GMT

    Last revised: April 28, 2014 at 23:02:29 GMT


    diff --git a/doc/reference.qbk b/doc/reference.qbk index cd0234c..d07c440 100644 --- a/doc/reference.qbk +++ b/doc/reference.qbk @@ -22,18 +22,24 @@ // (If T is of reference type, the parameters and results by reference are by value) - optional () ; ``[link reference_optional_constructor __GO_TO__]`` + optional () noexcept ; ``[link reference_optional_constructor __GO_TO__]`` - optional ( none_t ) ; ``[link reference_optional_constructor_none_t __GO_TO__]`` + optional ( none_t ) noexcept ; ``[link reference_optional_constructor_none_t __GO_TO__]`` optional ( T const& v ) ; ``[link reference_optional_constructor_value __GO_TO__]`` + + optional ( T&& v ) ; ``[link reference_optional_constructor_move_value __GO_TO__]`` // [new in 1.34] optional ( bool condition, T const& v ) ; ``[link reference_optional_constructor_bool_value __GO_TO__]`` optional ( optional const& rhs ) ; ``[link reference_optional_constructor_optional __GO_TO__]`` + + optional ( optional&& rhs ) noexcept(``['see below]``) ; ``[link reference_optional_move_constructor_optional __GO_TO__]`` template explicit optional ( optional const& rhs ) ; ``[link reference_optional_constructor_other_optional __GO_TO__]`` + + template explicit optional ( optional&& rhs ) ; ``[link reference_optional_move_constructor_other_optional __GO_TO__]`` template explicit optional ( InPlaceFactory const& f ) ; ``[link reference_optional_constructor_factory __GO_TO__]`` @@ -42,10 +48,16 @@ optional& operator = ( none_t ) ; ``[/[link reference_optional_operator_equal_none_t __GO_TO__]]`` optional& operator = ( T const& v ) ; ``[link reference_optional_operator_equal_value __GO_TO__]`` + + optional& operator = ( T&& v ) ; ``[link reference_optional_operator_move_equal_value __GO_TO__]`` optional& operator = ( optional const& rhs ) ; ``[link reference_optional_operator_equal_optional __GO_TO__]`` + + optional& operator = ( optional&& rhs ) noexcept(``['see below]``) ; ``[link reference_optional_operator_move_equal_optional __GO_TO__]`` template optional& operator = ( optional const& rhs ) ; ``[link reference_optional_operator_equal_other_optional __GO_TO__]`` + + template optional& operator = ( optional&& rhs ) ; ``[link reference_optional_operator_move_equal_other_optional __GO_TO__]`` template optional& operator = ( InPlaceFactory const& f ) ; ``[link reference_optional_operator_equal_factory __GO_TO__]`` @@ -68,7 +80,7 @@ operator unspecified-bool-type() const ; ``[link reference_optional_operator_bool __GO_TO__]`` - bool operator!() const ; ``[link reference_optional_operator_not __GO_TO__]`` + bool operator!() const noexcept ; ``[link reference_optional_operator_not __GO_TO__]`` // deprecated methods @@ -151,12 +163,11 @@ __SPACE__ [#reference_optional_constructor] -[: `optional::optional();`] +[: `optional::optional() noexcept;`] * [*Effect:] Default-Constructs an `optional`. * [*Postconditions:] `*this` is [_uninitialized]. -* [*Throws:] Nothing. -* Notes: T's default constructor [_is not] called. +* [*Notes:] T's default constructor [_is not] called. * [*Example:] `` optional def ; @@ -167,11 +178,10 @@ __SPACE__ [#reference_optional_constructor_none_t] -[: `optional::optional( none_t );`] +[: `optional::optional( none_t ) noexcept;`] * [*Effect:] Constructs an `optional` uninitialized. * [*Postconditions:] `*this` is [_uninitialized]. -* [*Throws:] Nothing. * [*Notes:] `T`'s default constructor [_is not] called. The expression `boost::none` denotes an instance of `boost::none_t` that can be used as the parameter. @@ -188,8 +198,9 @@ __SPACE__ [: `optional::optional( T const& v )`] +* [*Requires:] `is_copy_constructible::value` is `true`. * [*Effect:] Directly-Constructs an `optional`. -* [*Postconditions:] `*this` is [_initialized] and its value is a['copy] +* [*Postconditions:] `*this` is [_initialized] and its value is a ['copy] of `v`. * [*Throws:] Whatever `T::T( T const& )` throws. * [*Notes: ] `T::T( T const& )` is called. @@ -220,6 +231,33 @@ assert ( *opt == v ) ; assert (*opt == v); `` +__SPACE__ + +[#reference_optional_constructor_move_value] + +[: `optional::optional( T&& v )`] + +* [*Requires:] `is_move_constructible::value` is `true`. +* [*Effect:] Directly-Move-Constructs an `optional`. +* [*Postconditions:] `*this` is [_initialized] and its value is move-constructed from `v`. +* [*Throws:] Whatever `T::T( T&& )` throws. +* [*Notes: ] `T::T( T&& )` is called. +* [*Exception Safety:] Exceptions can only be thrown during +`T::T( T&& );` in that case, the state of `v` is determined by exception safety guarantees for `T::T(T&&)`. +* [*Example:] +`` +T v1, v2; +optional opt(std::move(v1)); +assert ( *opt == v2 ) ; +`` + +__SPACE__ + +[: `optional::optional( T&& ref ) = delete`] + +* [*Notes:] This constructor is deleted + + __SPACE__ [#reference_optional_constructor_bool_value] @@ -243,6 +281,7 @@ __SPACE__ [: `optional::optional( optional const& rhs );`] +* [*Requires:] `is_copy_constructible::value` is `true`. * [*Effect:] Copy-Constructs an `optional`. * [*Postconditions:] If rhs is initialized, `*this` is initialized and its value is a ['copy] of the value of `rhs`; else `*this` is uninitialized. @@ -299,6 +338,70 @@ assert ( *init2 == 3 ) ; __SPACE__ +[#reference_optional_move_constructor_optional] + +[: `optional::optional( optional&& rhs ) noexcept(`['see below]`);`] + +* [*Requires:] `is_move_constructible::value` is `true`. +* [*Effect:] Move-constructs an `optional`. +* [*Postconditions:] If `rhs` is initialized, `*this` is initialized and +its value is move constructed from `rhs`; else `*this` is uninitialized. +* [*Throws:] Whatever `T::T( T&& )` throws. +* [*Notes:] If `rhs` is initialized, `T::T( T && )` is called. The expression inside `noexcept` is equivalent to `is_nothrow_move_constructible::value`. +* [*Exception Safety:] Exceptions can only be thrown during +`T::T( T&& );` in that case, `rhs` remains initialized and the value of `*rhs` is determined by exception safety of `T::T(T&&)`. +* [*Example:] +`` +optional> uninit ; +assert (!uninit); + +optional> uinit2 ( std::move(uninit) ) ; +assert ( uninit2 == uninit ); + +optional> init( std::uniqye_ptr(new T(2)) ); +assert ( **init == T(2) ) ; + +optional> init2 ( std::move(init) ) ; +assert ( init ); +assert ( *init == nullptr ); +assert ( init2 ); +assert ( **init2 == T(2) ) ; +`` + +__SPACE__ + +[: `optional::optional( optional && rhs );`] + +* [*Effect:] Move-Constructs an `optional`. +* [*Postconditions:] If `rhs` is initialized, `*this` is initialized and its +value is another reference to the same object referenced by `*rhs`; else +`*this` is uninitialized. +* [*Throws:] Nothing. +* [*Notes:] If `rhs` is initialized, both `*this` and `*rhs` will reefer to the +same object (they alias). +* [*Example:] +`` +optional&> uninit ; +assert (!uninit); + +optional&> uinit2 ( std::move(uninit) ) ; +assert ( uninit2 == uninit ); + +std::unique_ptr v(new T(2)) ; +optional&> init(v); +assert ( *init == v ) ; + +optional&> init2 ( std::move(init) ) ; +assert ( *init2 == v ) ; + +*v = 3 ; + +assert ( **init == 3 ) ; +assert ( **init2 == 3 ) ; +`` + +__SPACE__ + [#reference_optional_constructor_other_optional] [: `template explicit optional::optional( optional const& rhs );`] @@ -323,6 +426,30 @@ assert( *y == 123 ) ; __SPACE__ +[#reference_optional_move_constructor_other_optional] + +[: `template explicit optional::optional( optional&& rhs );`] + +* [*Effect:] Move-constructs an `optional`. +* [*Postconditions:] If `rhs` is initialized, `*this` is initialized and its +value is move constructed from `*rhs`; else `*this` is +uninitialized. +* [*Throws:] Whatever `T::T( U&& )` throws. +* [*Notes: ] `T::T( U&& )` is called if `rhs` is initialized, which requires a +valid conversion from `U` to `T`. +* [*Exception Safety:] Exceptions can only be thrown during `T::T( U&& );` +in that case, `rhs` remains initialized and the value of `*rhs` is determined by exception safety guarantee of `T::T( U&& )`. +* [*Example:] +`` +optional x(123.4); +assert ( *x == 123.4 ) ; + +optional y(std::move(x)) ; +assert( *y == 123 ) ; +`` + +__SPACE__ + [#reference_optional_constructor_factory] [: `template explicit optional::optional( InPlaceFactory const& f );`] @@ -408,6 +535,42 @@ c = 4 ; assert ( *opt == 4 ) ; `` +__SPACE__ + +[#reference_optional_operator_move_equal_value] + +[: `optional& optional::operator= ( T&& rhs ) ;`] + +* [*Effect:] Moves the value `rhs` to an `optional`. +* [*Postconditions: ] `*this` is initialized and its value is moved from `rhs`. +* [*Throws:] Whatever `T::operator=( T&& )` or `T::T(T &&)` throws. +* [*Notes:] If `*this` was initialized, `T`'s move-assignment operator is used, +otherwise, its move-constructor is used. +* [*Exception Safety:] In the event of an exception, the initialization +state of `*this` is unchanged and its value unspecified as far as `optional` +is concerned (it is up to `T`'s `operator=()`). If `*this` is initially +uninitialized and `T`'s ['move constructor] fails, `*this` is left properly +uninitialized. +* [*Example:] +`` +T x; +optional def ; +optional opt(x) ; + +T y1, y2, yR; +def = std::move(y1) ; +assert ( *def == yR ) ; +opt = std::move(y2) ; +assert ( *opt == yR ) ; +`` + +__SPACE__ + +[: `optional& optional::operator= ( T&& rhs ) = delete;`] + +* [*Notes:] This assignment operator is deleted. + + __SPACE__ [#reference_optional_operator_equal_optional] @@ -471,6 +634,42 @@ assert ( *ora == 4 ) ; __SPACE__ +[#reference_optional_operator_move_equal_optional] + +[: `optional& optional::operator= ( optional&& rhs ) noexcept(`['see below]`);`] + +* [*Effect:] Move-assigns another `optional` to an `optional`. +* [*Postconditions:] If `rhs` is initialized, `*this` is initialized and +its value is moved from `*rhs`, `rhs` remains initialized; else `*this` is uninitialized. +* [*Throws:] Whatever `T::operator( T&& )` or `T::T( T && )` throws. +* [*Notes:] If both `*this` and `rhs` are initially initialized, `T`'s +['move assignment operator] is used. If `*this` is initially initialized but `rhs` is +uninitialized, `T`'s [destructor] is called. If `*this` is initially uninitialized +but `rhs` is initialized, `T`'s ['move constructor] is called. The expression inside `noexcept` is equivalent to `is_nothrow_move_constructible::value && is_nothrow_move_assignable::value`. +* [*Exception Safety:] In the event of an exception, the initialization state of +`*this` is unchanged and its value unspecified as far as optional is concerned +(it is up to `T`'s `operator=()`). If `*this` is initially uninitialized and +`T`'s ['move constructor] fails, `*this` is left properly uninitialized. +* [*Example:] +`` +optional opt(T(2)) ; +optional def ; + +opt = def ; +assert ( def ) ; +assert ( opt ) ; +assert ( *opt == T(2) ) ; +`` + +__SPACE__ + +[: `optional & optional::operator= ( optional&& rhs ) ;`] + +* [*Effect:] Same as `optional::operator= ( optional const& rhs )`. + +__SPACE__ + + [#reference_optional_operator_equal_other_optional] [: `template optional& optional::operator= ( optional const& rhs ) ;`] @@ -502,6 +701,37 @@ assert ( *opt1 == static_cast(v) ) ; __SPACE__ +[#reference_optional_operator_move_equal_other_optional] + +[: `template optional& optional::operator= ( optional&& rhs ) ;`] + +* [*Effect:] Move-assigns another convertible optional to an optional. +* [*Postconditions:] If `rhs` is initialized, `*this` is initialized and +its value is moved from the value of `rhs`; else +`*this` is uninitialized. +* [*Throws:] Whatever `T::operator=( U&& )` or `T::T( U&& )` throws. +* [*Notes:] If both `*this` and `rhs` are initially initialized, `T`'s +[' assignment operator] (from `U&&`) is used. If `*this` is initially initialized +but `rhs` is uninitialized, `T`'s ['destructor] is called. If `*this` is +initially uninitialized but `rhs` is initialized, `T`'s ['converting constructor] +(from `U&&`) is called. +* [*Exception Safety:] In the event of an exception, the initialization state +of `*this` is unchanged and its value unspecified as far as optional is +concerned (it is up to `T`'s `operator=()`). If `*this` is initially +uninitialized and `T`'s converting constructor fails, `*this` is left properly +uninitialized. +* [*Example:] +`` +T v; +optional opt0(v); +optional opt1; + +opt1 = std::move(opt0) ; +assert ( *opt1 == static_cast(v) ) ; +`` + +__SPACE__ + [#reference_optional_operator_equal_factory] [: `template optional& optional::operator=( InPlaceFactory const& f );`] @@ -543,7 +773,7 @@ __SPACE__ [: `inline T const& get ( optional const& ) ;`] [: `inline T& get ( optional &) ;`] -* [*Requirements:] `*this` is initialized +* [*Requires:] `*this` is initialized * [*Returns:] A reference to the contained value * [*Throws:] Nothing. * [*Notes:] The requirement is asserted via `BOOST_ASSERT()`. @@ -593,7 +823,7 @@ __SPACE__ [: `inline T const& get ( optional const& ) ;`] [: `inline T& get ( optional &) ;`] -* [*Requirements: ] `*this` is initialized +* [*Requires: ] `*this` is initialized * [*Returns:] [_The] reference contained. * [*Throws:] Nothing. * [*Notes:] The requirement is asserted via `BOOST_ASSERT()`. @@ -641,7 +871,7 @@ __SPACE__ [: `T const* optional::operator ->() const ;`] [: `T* optional::operator ->() ;`] -* [*Requirements: ] `*this` is initialized. +* [*Requires: ] `*this` is initialized. * [*Returns:] A pointer to the contained value. * [*Throws:] Nothing. * [*Notes:] The requirement is asserted via `BOOST_ASSERT()`. @@ -675,10 +905,9 @@ __SPACE__ [#reference_optional_operator_not] -[: `bool optional::operator!() ;`] +[: `bool optional::operator!() noexcept ;`] * [*Returns:] If `*this` is uninitialized, `true`; else `false`. -* [*Throws:] Nothing. * [*Notes:] This operator is provided for those compilers which can't use the ['unspecified-bool-type operator] in certain boolean contexts. * [*Example:] @@ -852,21 +1081,21 @@ __SPACE__ [#reference_swap_optional_optional] -[: `void swap ( optional& x, optional& y );`] +[: `void swap ( optional& x, optional& y ) ;`] * [*Effect:] If both `x` and `y` are initialized, calls `swap(*x,*y)` using `std::swap`. If only one is initialized, say `x`, calls: `y.reset(*x); x.reset();` If none is initialized, does nothing. * [*Postconditions:] The states of `x` and `y` interchanged. * [*Throws:] If both are initialized, whatever `swap(T&,T&)` throws. If only -one is initialized, whatever `T::T ( T const& )` throws. +one is initialized, whatever `T::T ( T&& )` throws. * [*Notes:] If both are initialized, `swap(T&,T&)` is used unqualified but with `std::swap` introduced in scope. -If only one is initialized, `T::~T()` and `T::T( T const& )` is called. +If only one is initialized, `T::~T()` and `T::T( T&& )` is called. * [*Exception Safety:] If both are initialized, this operation has the exception safety guarantees of `swap(T&,T&)`. If only one is initialized, it has the same basic guarantee as -`optional::reset( T const& )`. +`optional::operator= ( T&& )`. * [*Example:] `` T x(12); diff --git a/doc/special_cases.qbk b/doc/special_cases.qbk index 681e517..0f6f22b 100644 --- a/doc/special_cases.qbk +++ b/doc/special_cases.qbk @@ -21,6 +21,13 @@ will nonetheless reefer to the same object. * Value-access will actually provide access to the referenced object rather than the reference itself. +[heading Rvalue references] + +Rvalue references and lvalue references to const have the ability in C++ to extend the life time of a temporary they bind to. Optional references do not have this capability, therefore to avoid surprising effects it is not possible to initialize an optional references from a temporary. Optional rvalue references are disabled altogether. Also, the initialization and assignment of an optional reference to const from rvalue reference is disabled. + + const int& i = 1; // legal + optional oi = 1; // illegal + [endsect] [section Rebinding semantics for assignment of optional references] diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index bbbe3be..d191462 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -99,8 +101,7 @@ class in_place_factory_base ; class typed_in_place_factory_base ; // This forward is needed to refer to namespace scope swap from the member swap -template void swap ( optional& x, optional& y ) - BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value); +template void swap ( optional& x, optional& y ); namespace optional_detail { @@ -1242,6 +1243,37 @@ struct swap_selector } }; +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +template<> +struct swap_selector +{ + template + static void optional_swap ( optional& x, optional& y ) + //BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && BOOST_NOEXCEPT_EXPR(boost::swap(*x, *y))) + { + if(x) + { + if (y) + { + boost::swap(*x, *y); + } + else + { + y = boost::move(*x); + x = boost::none; + } + } + else + { + if (y) + { + x = boost::move(*y); + y = boost::none; + } + } + } +}; +#else template<> struct swap_selector { @@ -1268,6 +1300,7 @@ struct swap_selector } } }; +#endif // !defined BOOST_NO_CXX11_RVALUE_REFERENCES } // namespace optional_detail @@ -1275,7 +1308,7 @@ template struct optional_swap_should_use_default_constructor : has_nothrow_default_constructor {} ; template inline void swap ( optional& x, optional& y ) - BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value) + //BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && BOOST_NOEXCEPT_EXPR(boost::swap(*x, *y))) { optional_detail::swap_selector::value>::optional_swap(x, y); } diff --git a/include/boost/optional/optional_fwd.hpp b/include/boost/optional/optional_fwd.hpp index ade503d..29cee49 100644 --- a/include/boost/optional/optional_fwd.hpp +++ b/include/boost/optional/optional_fwd.hpp @@ -16,16 +16,13 @@ #ifndef BOOST_OPTIONAL_OPTIONAL_FWD_FLC_19NOV2002_HPP #define BOOST_OPTIONAL_OPTIONAL_FWD_FLC_19NOV2002_HPP #include -#include -#include namespace boost { template class optional ; -template void swap ( optional& , optional& ) - BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible::value && ::boost::is_nothrow_move_assignable::value); +template void swap ( optional& , optional& ); template struct optional_swap_should_use_default_constructor ; diff --git a/test/optional_test_move.cpp b/test/optional_test_move.cpp index a2d20c1..affa07f 100644 --- a/test/optional_test_move.cpp +++ b/test/optional_test_move.cpp @@ -314,6 +314,35 @@ void test_move_ctor_from_optional_U() BOOST_CHECK(b2->val == 4); } +void test_swap() +{ + optional a((MoveOnly(2))); + optional b((MoveOnly(3))); + swap(a, b); + + BOOST_CHECK(a->val == 3); + BOOST_CHECK(b->val == 2); +} + +void test_optional_ref_to_movables() +{ + MoveOnly m(3); + optional orm = m; + orm->val = 2; + BOOST_CHECK(m.val == 2); + + optional orm2 = orm; + orm2->val = 1; + BOOST_CHECK(m.val == 1); + BOOST_CHECK(orm->val == 1); + + optional orm3 = boost::move(orm); + orm3->val = 4; + BOOST_CHECK(m.val == 4); + BOOST_CHECK(orm->val == 4); + BOOST_CHECK(orm2->val == 4); +} + // these 4 classes have different noexcept signatures in move operations struct NothrowBoth { NothrowBoth(NothrowBoth&&) BOOST_NOEXCEPT_IF(true) {}; @@ -370,6 +399,8 @@ int test_main( int, char* [] ) test_move_assign_from_optional_T(); test_move_assign_from_optional_U(); test_with_move_only(); + test_optional_ref_to_movables(); + test_swap(); #endif } catch ( ... )