2
0
mirror of https://github.com/boostorg/variant.git synced 2026-02-02 21:32:11 +00:00

Merge from trunk: added basic rvalue support and marked some functions with BOOST_NOEXCEPT (fixes #7620)

[SVN r81455]
This commit is contained in:
Antony Polukhin
2012-11-21 14:47:12 +00:00
parent 5b34fe155f
commit 591ae9bf26
5 changed files with 538 additions and 20 deletions

View File

@@ -13,6 +13,7 @@
#ifndef BOOST_VARIANT_DETAIL_BACKUP_HOLDER_HPP
#define BOOST_VARIANT_DETAIL_BACKUP_HOLDER_HPP
#include "boost/config.hpp"
#include "boost/assert.hpp"
namespace boost {
@@ -32,7 +33,7 @@ public: // structors
delete backup_;
}
explicit backup_holder(T* backup)
explicit backup_holder(T* backup) BOOST_NOEXCEPT
: backup_(backup)
{
}
@@ -53,7 +54,7 @@ public: // modifiers
return *this;
}
void swap(backup_holder& rhs)
void swap(backup_holder& rhs) BOOST_NOEXCEPT
{
T* tmp = rhs.backup_;
rhs.backup_ = this->backup_;
@@ -83,7 +84,7 @@ backup_holder<T>::backup_holder(const backup_holder&)
}
template <typename T>
void swap(backup_holder<T>& lhs, backup_holder<T>& rhs)
void swap(backup_holder<T>& lhs, backup_holder<T>& rhs) BOOST_NOEXCEPT
{
lhs.swap(rhs);
}

View File

@@ -82,6 +82,8 @@ public: // metafunction result
} // namespace detail
#ifdef BOOST_NO_RVALUE_REFERENCES
template <typename T>
inline
typename detail::move_type<T>::type
@@ -93,6 +95,12 @@ move(T& source)
return move_t(source);
}
#else
using std::move;
#endif
//////////////////////////////////////////////////////////////////////////
// class template return_t
//

View File

@@ -51,7 +51,9 @@
#include "boost/type_traits/has_nothrow_copy.hpp"
#include "boost/type_traits/is_const.hpp"
#include "boost/type_traits/is_same.hpp"
#include "boost/type_traits/is_rvalue_reference.hpp"
#include "boost/utility/enable_if.hpp"
#include "boost/utility/declval.hpp"
#include "boost/variant/recursive_wrapper_fwd.hpp"
#include "boost/variant/static_visitor.hpp"
@@ -338,7 +340,7 @@ class known_get
public: // visitor interface
T& operator()(T& operand) const
T& operator()(T& operand) const BOOST_NOEXCEPT
{
return operand;
}
@@ -397,7 +399,7 @@ private: // representation
public: // structors
explicit copy_into(void* storage)
explicit copy_into(void* storage) BOOST_NOEXCEPT
: storage_(storage)
{
}
@@ -430,6 +432,46 @@ public: // internal visitor interface
};
///////////////////////////////////////////////////////////////////////////////
// (detail) class move_into
//
// Internal visitor that moves the value it visits into the given buffer.
//
#ifndef BOOST_NO_RVALUE_REFERENCES
class move_into
: public static_visitor<>
{
private: // representation
void* storage_;
public: // structors
explicit move_into(void* storage) BOOST_NOEXCEPT
: storage_(storage)
{
}
public: // internal visitor interface
template <typename T>
BOOST_VARIANT_AUX_RETURN_VOID_TYPE
internal_visit(boost::detail::variant::backup_holder<T>& operand, long) const
{
new(storage_) T( ::boost::detail::variant::move(operand.get()) );
BOOST_VARIANT_AUX_RETURN_VOID;
}
template <typename T>
BOOST_VARIANT_AUX_RETURN_VOID_TYPE
internal_visit(T& operand, int) const BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(boost::declval<T>())))
{
new(storage_) T(::boost::detail::variant::move(operand));
BOOST_VARIANT_AUX_RETURN_VOID;
}
};
#endif
///////////////////////////////////////////////////////////////////////////////
// (detail) class assign_storage
//
@@ -445,7 +487,7 @@ private: // representation
public: // structors
explicit assign_storage(const void* rhs_storage)
explicit assign_storage(const void* rhs_storage) BOOST_NOEXCEPT
: rhs_storage_(rhs_storage)
{
}
@@ -487,6 +529,63 @@ public: // internal visitor interfaces
};
///////////////////////////////////////////////////////////////////////////////
// (detail) class move_storage
//
// Internal visitor that moves the given storage (which must be a
// constructed value of the same type) to the value it visits.
//
struct move_storage
: public static_visitor<>
{
private: // representation
void* rhs_storage_;
public: // structors
explicit move_storage(void* rhs_storage) BOOST_NOEXCEPT
: rhs_storage_(rhs_storage)
{
}
public: // internal visitor interfaces
template <typename T>
BOOST_VARIANT_AUX_RETURN_VOID_TYPE
internal_visit(backup_holder<T>& lhs_content, long) const
{
lhs_content.get()
= ::boost::detail::variant::move(static_cast<backup_holder<T>* >(rhs_storage_)->get());
BOOST_VARIANT_AUX_RETURN_VOID;
}
template <typename T>
BOOST_VARIANT_AUX_RETURN_VOID_TYPE
internal_visit(const backup_holder<T>& lhs_content, long) const
{
lhs_content.get()
= ::boost::detail::variant::move(static_cast<backup_holder<T>* >(rhs_storage_)->get());
BOOST_VARIANT_AUX_RETURN_VOID;
}
template <typename T>
BOOST_VARIANT_AUX_RETURN_VOID_TYPE
internal_visit(T& lhs_content, int) const
{
// NOTE TO USER :
// Compile error here indicates one of variant's bounded types does
// not meet the requirements of the Assignable concept. Thus,
// variant is not Assignable.
//
// Hint: Are any of the bounded types const-qualified or references?
//
lhs_content = ::boost::detail::variant::move(*static_cast<T* >(rhs_storage_));
BOOST_VARIANT_AUX_RETURN_VOID;
}
};
///////////////////////////////////////////////////////////////////////////////
// (detail) class direct_assigner
//
@@ -504,7 +603,7 @@ private: // representation
public: // structors
explicit direct_assigner(const T& rhs)
explicit direct_assigner(const T& rhs) BOOST_NOEXCEPT
: rhs_(rhs)
{
}
@@ -520,7 +619,7 @@ public: // visitor interface
}
template <typename U>
bool operator()(U&)
bool operator()(U&) BOOST_NOEXCEPT
{
return false;
}
@@ -559,6 +658,65 @@ private:
#endif
};
///////////////////////////////////////////////////////////////////////////////
// (detail) class direct_mover
//
// Generic static visitor that: if and only if the visited value is of the
// specified type, move assigns the given value to the visited value and returns
// true; else returns false.
//
template <typename T>
class direct_mover
: public static_visitor<bool>
{
private: // representation
T& rhs_;
public: // structors
explicit direct_mover(T& rhs) BOOST_NOEXCEPT
: rhs_(rhs)
{
}
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300)
public: // visitor interface
bool operator()(T& lhs)
{
lhs = ::boost::detail::variant::move(rhs_);
return true;
}
template <typename U>
bool operator()(U&) BOOST_NOEXCEPT
{
return false;
}
#else // MSVC6
public: // visitor interface
template <typename U>
bool operator()(U& lhs)
{
// MSVC6 can not use direct_mover class
return direct_assigner(rhs_)(lhs);
}
#endif // MSVC6 workaround
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
private:
// silence MSVC warning C4512: assignment operator could not be generated
direct_mover& operator= (direct_mover const&);
#endif
};
///////////////////////////////////////////////////////////////////////////////
// (detail) class backup_assigner
//
@@ -745,7 +903,7 @@ class reflect
public: // visitor interfaces
template <typename T>
const std::type_info& operator()(const T&) const
const std::type_info& operator()(const T&) const BOOST_NOEXCEPT
{
return typeid(T);
}
@@ -772,7 +930,7 @@ private: // representation
public: // structors
explicit comparer(const Variant& lhs)
explicit comparer(const Variant& lhs) BOOST_NOEXCEPT
: lhs_(lhs)
{
}
@@ -844,7 +1002,7 @@ public: // visitor typedefs
public: // structors
explicit invoke_visitor(Visitor& visitor)
explicit invoke_visitor(Visitor& visitor) BOOST_NOEXCEPT
: visitor_(visitor)
{
}
@@ -1170,26 +1328,26 @@ private: // helpers, for representation (below)
which_t which_;
storage_t storage_;
void indicate_which(int which_arg)
void indicate_which(int which_arg) BOOST_NOEXCEPT
{
which_ = static_cast<which_t>( which_arg );
}
void indicate_backup_which(int which_arg)
void indicate_backup_which(int which_arg) BOOST_NOEXCEPT
{
which_ = static_cast<which_t>( -(which_arg + 1) );
}
private: // helpers, for queries (below)
bool using_backup() const
bool using_backup() const BOOST_NOEXCEPT
{
return which_ < 0;
}
public: // queries
int which() const
int which() const BOOST_NOEXCEPT
{
// If using heap backup...
if (using_backup())
@@ -1222,7 +1380,7 @@ public: // structors
destroy_content();
}
variant()
variant() BOOST_NOEXCEPT_IF(boost::has_nothrow_constructor<internal_T0>::type::value)
{
// NOTE TO USER :
// Compile error from here indicates that the first bound
@@ -1244,7 +1402,7 @@ private: // helpers, for structors, cont. (below)
public: // structors
explicit convert_copy_into(void* storage)
explicit convert_copy_into(void* storage) BOOST_NOEXCEPT
: storage_(storage)
{
}
@@ -1454,6 +1612,18 @@ public: // structors, cont.
// ...and activate the *this's primary storage on success:
indicate_which(operand.which());
}
#ifndef BOOST_NO_RVALUE_REFERENCES
variant(variant&& operand)
{
// Move the value of operand into *this...
detail::variant::move_into visitor( storage_.address() );
operand.internal_apply_visitor(visitor);
// ...and activate the *this's primary storage on success:
indicate_which(operand.which());
}
#endif
private: // helpers, for modifiers (below)
@@ -1478,7 +1648,7 @@ private: // helpers, for modifiers (below)
public: // structors
assigner(variant& lhs, int rhs_which)
assigner(variant& lhs, int rhs_which) BOOST_NOEXCEPT
: lhs_(lhs)
, rhs_which_(rhs_which)
{
@@ -1605,8 +1775,153 @@ private: // helpers, for modifiers (below)
assigner& operator= (assigner const&);
#endif
};
friend class assigner;
#ifndef BOOST_NO_RVALUE_REFERENCES
// class move_assigner
//
// Internal visitor that "move assigns" the visited value to the given variant
// by appropriate destruction and move-construction.
//
class move_assigner
: public static_visitor<>
{
private: // representation
variant& lhs_;
int rhs_which_;
public: // structors
move_assigner(variant& lhs, int rhs_which) BOOST_NOEXCEPT
: lhs_(lhs)
, rhs_which_(rhs_which)
{
}
private: // helpers, for internal visitor interface (below)
template <typename RhsT, typename B1, typename B2>
void assign_impl(
RhsT& rhs_content
, mpl::true_// has_nothrow_copy
, mpl::false_// has_nothrow_move_constructor
, B2// has_fallback_type
)
{
// Destroy lhs's content...
lhs_.destroy_content(); // nothrow
// ...copy rhs content into lhs's storage...
new(lhs_.storage_.address())
RhsT( rhs_content ); // nothrow
// ...and indicate new content type:
lhs_.indicate_which(rhs_which_); // nothrow
}
template <typename RhsT, typename B>
void assign_impl(
RhsT& rhs_content
, mpl::true_// has_nothrow_copy
, mpl::true_// has_nothrow_move_constructor
, B// has_fallback_type
)
{
// ...destroy lhs's content...
lhs_.destroy_content(); // nothrow
// ...move the rhs_content into lhs's storage...
new(lhs_.storage_.address())
RhsT( detail::variant::move(rhs_content) ); // nothrow
// ...and indicate new content type:
lhs_.indicate_which(rhs_which_); // nothrow
}
template <typename RhsT>
void assign_impl(
RhsT& rhs_content
, mpl::false_// has_nothrow_copy
, mpl::false_// has_nothrow_move_constructor
, mpl::true_// has_fallback_type
)
{
// Destroy lhs's content...
lhs_.destroy_content(); // nothrow
try
{
// ...and attempt to copy rhs's content into lhs's storage:
new(lhs_.storage_.address())
RhsT( detail::variant::move(rhs_content) );
}
catch (...)
{
// In case of failure, default-construct fallback type in lhs's storage...
new (lhs_.storage_.address())
fallback_type_; // nothrow
// ...indicate construction of fallback type...
lhs_.indicate_which(
BOOST_MPL_AUX_VALUE_WKND(fallback_type_index_)::value
); // nothrow
// ...and rethrow:
throw;
}
// In the event of success, indicate new content type:
lhs_.indicate_which(rhs_which_); // nothrow
}
template <typename RhsT>
void assign_impl(
const RhsT& rhs_content
, mpl::false_// has_nothrow_copy
, mpl::false_// has_nothrow_move_constructor
, mpl::false_// has_fallback_type
)
{
detail::variant::backup_assigner<wknd_self_t>
visitor(lhs_, rhs_which_, rhs_content);
lhs_.internal_apply_visitor(visitor);
}
public: // internal visitor interfaces
template <typename RhsT>
BOOST_VARIANT_AUX_RETURN_VOID_TYPE
internal_visit(RhsT& rhs_content, int)
{
typedef typename detail::variant::has_nothrow_move_constructor<RhsT>::type
nothrow_move_constructor;
typedef typename mpl::or_< // reduces compile-time
nothrow_move_constructor
, has_nothrow_copy<RhsT>
>::type nothrow_copy;
assign_impl(
rhs_content
, nothrow_copy()
, nothrow_move_constructor()
, has_fallback_type_()
);
BOOST_VARIANT_AUX_RETURN_VOID;
}
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
private:
// silence MSVC warning C4512: assignment operator could not be generated
move_assigner& operator= (move_assigner const&);
#endif
};
friend class move_assigner;
#endif // BOOST_NO_RVALUE_REFERENCES
void variant_assign(const variant& rhs)
{
@@ -1625,6 +1940,25 @@ private: // helpers, for modifiers (below)
}
}
#ifndef BOOST_NO_RVALUE_REFERENCES
void variant_assign(variant&& rhs)
{
// If the contained types are EXACTLY the same...
if (which_ == rhs.which_)
{
// ...then move rhs's storage to lhs's content:
detail::variant::move_storage visitor(rhs.storage_.address());
this->internal_apply_visitor(visitor);
}
else
{
// Otherwise, perform general (move-based) variant assignment:
move_assigner visitor(*this, rhs.which());
rhs.internal_apply_visitor(visitor);
}
}
#endif // BOOST_NO_RVALUE_REFERENCES
private: // helpers, for modifiers (below)
template <typename T>
@@ -1645,8 +1979,37 @@ private: // helpers, for modifiers (below)
}
}
#ifndef BOOST_NO_RVALUE_REFERENCES
template <typename T>
void move_assign(T&& rhs)
{
// If direct T-to-T move assignment is not possible...
detail::variant::direct_mover<T> direct_move(rhs);
if (this->apply_visitor(direct_move) == false)
{
// ...then convert rhs to variant and assign:
//
// While potentially inefficient, the following construction of a
// variant allows T as any type convertible to one of the bounded
// types without excessive code redundancy.
//
variant temp( detail::variant::move(rhs) );
variant_assign( detail::variant::move(temp) );
}
}
#endif // BOOST_NO_RVALUE_REFERENCES
public: // modifiers
#ifndef BOOST_NO_RVALUE_REFERENCES
template <class T>
typename boost::enable_if<boost::is_rvalue_reference<T&&>, variant& >::type operator=(T&& rhs)
{
move_assign( detail::variant::move(rhs) );
return *this;
}
#endif // BOOST_NO_RVALUE_REFERENCES
template <typename T>
variant& operator=(const T& rhs)
{
@@ -1661,6 +2024,14 @@ public: // modifiers
return *this;
}
#ifndef BOOST_NO_RVALUE_REFERENCES
variant& operator=(variant&& rhs)
{
variant_assign( detail::variant::move(rhs) );
return *this;
}
#endif // BOOST_NO_RVALUE_REFERENCES
void swap(variant& rhs)
{
// If the contained types are the same...
@@ -1685,7 +2056,7 @@ public: // queries
// NOTE: member which() defined above.
//
bool empty() const
bool empty() const BOOST_NOEXCEPT
{
return false;
}

View File

@@ -33,6 +33,7 @@ test-suite variant
[ run variant_comparison_test.cpp ]
[ run variant_visit_test.cpp ]
[ run hash_variant_test.cpp ]
[ run rvalue_test.cpp ]
;

137
test/rvalue_test.cpp Normal file
View File

@@ -0,0 +1,137 @@
//-----------------------------------------------------------------------------
// boost-libs variant/test/rvalue_test.cpp source file
// See http://www.boost.org for updates, documentation, and revision history.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2012
// Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "boost/config.hpp"
#include "boost/test/minimal.hpp"
#include "boost/variant.hpp"
// This test requires BOOST_HAS_RVALUE_REFS
#ifndef BOOST_HAS_RVALUE_REFS
void run()
{
BOOST_CHECK(true);
}
#else
class move_copy_conting_class {
public:
static unsigned int moves_count;
static unsigned int copy_count;
move_copy_conting_class(){}
move_copy_conting_class(move_copy_conting_class&&) {
++ moves_count;
}
move_copy_conting_class& operator=(move_copy_conting_class&&) {
++ moves_count;
return *this;
}
move_copy_conting_class(const move_copy_conting_class&) {
++ copy_count;
}
move_copy_conting_class& operator=(const move_copy_conting_class&) {
++ copy_count;
return *this;
}
};
unsigned int move_copy_conting_class::moves_count = 0;
unsigned int move_copy_conting_class::copy_count = 0;
void run()
{
typedef boost::variant<int, move_copy_conting_class> variant_I_type;
variant_I_type v1, v2;
// Assuring that `move_copy_conting_class` was not created
BOOST_CHECK(move_copy_conting_class::copy_count == 0);
BOOST_CHECK(move_copy_conting_class::moves_count == 0);
v1 = move_copy_conting_class();
// Assuring that `move_copy_conting_class` was moved at least once
BOOST_CHECK(move_copy_conting_class::moves_count != 0);
unsigned int total_count = move_copy_conting_class::moves_count + move_copy_conting_class::copy_count;
move_copy_conting_class var;
v1 = 0;
move_copy_conting_class::moves_count = 0;
move_copy_conting_class::copy_count = 0;
v1 = var;
// Assuring that move assignment operator moves/copyes value not more times than copy assignment operator
BOOST_CHECK(total_count <= move_copy_conting_class::moves_count + move_copy_conting_class::copy_count);
move_copy_conting_class::moves_count = 0;
move_copy_conting_class::copy_count = 0;
v2 = static_cast<variant_I_type&&>(v1);
// Assuring that `move_copy_conting_class` in v1 was moved at least once and was not copied
BOOST_CHECK(move_copy_conting_class::moves_count != 0);
BOOST_CHECK(move_copy_conting_class::copy_count == 0);
v1 = move_copy_conting_class();
move_copy_conting_class::moves_count = 0;
move_copy_conting_class::copy_count = 0;
v2 = static_cast<variant_I_type&&>(v1);
// Assuring that `move_copy_conting_class` in v1 was moved at least once and was not copied
BOOST_CHECK(move_copy_conting_class::moves_count != 0);
BOOST_CHECK(move_copy_conting_class::copy_count == 0);
total_count = move_copy_conting_class::moves_count + move_copy_conting_class::copy_count;
move_copy_conting_class::moves_count = 0;
move_copy_conting_class::copy_count = 0;
v1 = v2;
// Assuring that move assignment operator moves/copyes value not more times than copy assignment operator
BOOST_CHECK(total_count <= move_copy_conting_class::moves_count + move_copy_conting_class::copy_count);
typedef boost::variant<move_copy_conting_class, int> variant_II_type;
variant_II_type v3;
move_copy_conting_class::moves_count = 0;
move_copy_conting_class::copy_count = 0;
v1 = static_cast<variant_II_type&&>(v3);
// Assuring that `move_copy_conting_class` in v3 was moved at least once (v1 and v3 have different types)
BOOST_CHECK(move_copy_conting_class::moves_count != 0);
move_copy_conting_class::moves_count = 0;
move_copy_conting_class::copy_count = 0;
v2 = static_cast<variant_I_type&&>(v1);
// Assuring that `move_copy_conting_class` in v1 was moved at least once (v1 and v3 have different types)
BOOST_CHECK(move_copy_conting_class::moves_count != 0);
move_copy_conting_class::moves_count = 0;
move_copy_conting_class::copy_count = 0;
variant_I_type v5(static_cast<variant_I_type&&>(v1));
// Assuring that `move_copy_conting_class` in v1 was moved at least once and was not copied
BOOST_CHECK(move_copy_conting_class::moves_count != 0);
BOOST_CHECK(move_copy_conting_class::copy_count == 0);
total_count = move_copy_conting_class::moves_count + move_copy_conting_class::copy_count;
move_copy_conting_class::moves_count = 0;
move_copy_conting_class::copy_count = 0;
variant_I_type v6(v1);
// Assuring that move constructor moves/copyes value not more times than copy constructor
BOOST_CHECK(total_count <= move_copy_conting_class::moves_count + move_copy_conting_class::copy_count);
}
#endif
int test_main(int , char* [])
{
run();
return 0;
}