Skip to content

Commit

Permalink
Merge pull request #160 from mlogan/master
Browse files Browse the repository at this point in the history
Fix the noexcept specifications for move assignment and conversion.
  • Loading branch information
artemp committed Jun 4, 2018
2 parents 5eee328 + 9c81bef commit 256ddd5
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ out/%.o: test/t/%.cpp Makefile $(ALL_HEADERS)
mkdir -p ./out
$(CXX) -c -o $@ $< -Iinclude -isystem test/include $(FINAL_CXXFLAGS)

out/unit: out/unit.o out/binary_visitor_1.o out/binary_visitor_2.o out/binary_visitor_3.o out/binary_visitor_4.o out/binary_visitor_5.o out/binary_visitor_6.o out/issue21.o out/issue122.o out/mutating_visitor.o out/optional.o out/recursive_wrapper.o out/sizeof.o out/unary_visitor.o out/variant.o out/variant_alternative.o
out/unit: out/unit.o out/binary_visitor_1.o out/binary_visitor_2.o out/binary_visitor_3.o out/binary_visitor_4.o out/binary_visitor_5.o out/binary_visitor_6.o out/issue21.o out/issue122.o out/mutating_visitor.o out/optional.o out/recursive_wrapper.o out/sizeof.o out/unary_visitor.o out/variant.o out/variant_alternative.o out/nothrow_move.o
mkdir -p ./out
$(CXX) -o $@ $^ $(LDFLAGS)

Expand Down
12 changes: 10 additions & 2 deletions include/mapbox/variant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,9 @@ class variant

public:
VARIANT_INLINE variant<Types...>& operator=(variant<Types...>&& other)
// note we check for nothrow-constructible, not nothrow-assignable, since
// move_assign uses move-construction via placement new.
noexcept(detail::conjunction<std::is_nothrow_move_constructible<Types>...>::value)
{
move_assign(std::move(other));
return *this;
Expand All @@ -650,8 +653,13 @@ class variant

// conversions
// move-assign
template <typename T>
VARIANT_INLINE variant<Types...>& operator=(T&& rhs) noexcept
template <typename T, typename Traits = detail::value_traits<T, Types...>,
typename Enable = typename std::enable_if<Traits::is_valid && !std::is_same<variant<Types...>, typename Traits::value_type>::value>::type >
VARIANT_INLINE variant<Types...>& operator=(T&& rhs)
// not that we check is_nothrow_constructible<T>, not is_nothrow_move_assignable<T>,
// since we construct a temporary
noexcept(std::is_nothrow_constructible<typename Traits::target_type, T&&>::value
&& std::is_nothrow_move_assignable<variant<Types...>>::value)
{
variant<Types...> temp(std::forward<T>(rhs));
move_assign(std::move(temp));
Expand Down
66 changes: 66 additions & 0 deletions test/t/nothrow_move.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include <typeinfo>
#include <utility>

#include <mapbox/variant.hpp>

using namespace mapbox;

namespace test {

struct t_noexcept_true_1 {
t_noexcept_true_1(t_noexcept_true_1&&) noexcept = default;
t_noexcept_true_1& operator=(t_noexcept_true_1&&) noexcept = default;
};

struct t_noexcept_true_2 {
t_noexcept_true_2(t_noexcept_true_2&&) noexcept = default;
t_noexcept_true_2& operator=(t_noexcept_true_2&&) noexcept = default;
};

struct t_noexcept_false_1 {
t_noexcept_false_1(t_noexcept_false_1&&) noexcept(false) {}
t_noexcept_false_1& operator=(t_noexcept_false_1&&) noexcept(false) { return *this; }
};

using should_be_no_throw_copyable = util::variant<t_noexcept_true_1, t_noexcept_true_2>;
static_assert(std::is_nothrow_move_assignable<should_be_no_throw_copyable>::value,
"variants with no-throw move assignable types should be "
"no-throw move nothrow assignable");

using should_be_no_throw_assignable = util::variant<t_noexcept_true_1, t_noexcept_true_2>;
static_assert(std::is_nothrow_move_constructible<should_be_no_throw_assignable>::value,
"variants with no-throw move assignable types should be "
"no-throw move nothrow assignable");

using should_not_be_no_throw_copyable = util::variant<t_noexcept_true_1, t_noexcept_false_1>;
static_assert(not std::is_nothrow_move_assignable<should_not_be_no_throw_copyable>::value,
"variants with no-throw move assignable types should be "
"no-throw move nothrow assignable");

using should_not_be_no_throw_assignable = util::variant<t_noexcept_true_1, t_noexcept_false_1>;
static_assert(not std::is_nothrow_move_constructible<should_not_be_no_throw_assignable>::value,
"variants with no-throw move assignable types should be "
"no-throw move nothrow assignable");


// this type cannot be nothrow converted from either of its types, even the nothrow moveable one,
// because the conversion operator moves the whole variant.
using convertable_test_type = util::variant<t_noexcept_true_1, t_noexcept_false_1>;

// this type can be nothrow converted from either of its types.
using convertable_test_type_2 = util::variant<t_noexcept_true_1, t_noexcept_true_2>;

static_assert(not std::is_nothrow_assignable<convertable_test_type, t_noexcept_true_1>::value,
"variants with noexcept(true) move constructible types should be nothrow-convertible "
"from those types only IF the variant itself is nothrow_move_assignable");

static_assert(not std::is_nothrow_assignable<convertable_test_type, t_noexcept_false_1>::value,
"variants with noexcept(false) move constructible types should not be nothrow-convertible "
"from those types");

static_assert(std::is_nothrow_assignable<convertable_test_type_2, t_noexcept_true_2>::value,
"variants with noexcept(true) move constructible types should be nothrow-convertible "
"from those types only IF the variant itself is nothrow_move_assignable");


} // namespace test

0 comments on commit 256ddd5

Please sign in to comment.