Skip to content

Commit 256ddd5

Browse files
authoredJun 4, 2018
Merge pull request #160 from mlogan/master
Fix the noexcept specifications for move assignment and conversion.
2 parents 5eee328 + 9c81bef commit 256ddd5

File tree

3 files changed

+77
-3
lines changed

3 files changed

+77
-3
lines changed
 

‎Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ out/%.o: test/t/%.cpp Makefile $(ALL_HEADERS)
9999
mkdir -p ./out
100100
$(CXX) -c -o $@ $< -Iinclude -isystem test/include $(FINAL_CXXFLAGS)
101101

102-
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
102+
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
103103
mkdir -p ./out
104104
$(CXX) -o $@ $^ $(LDFLAGS)
105105

‎include/mapbox/variant.hpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,9 @@ class variant
637637

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

651654
// conversions
652655
// move-assign
653-
template <typename T>
654-
VARIANT_INLINE variant<Types...>& operator=(T&& rhs) noexcept
656+
template <typename T, typename Traits = detail::value_traits<T, Types...>,
657+
typename Enable = typename std::enable_if<Traits::is_valid && !std::is_same<variant<Types...>, typename Traits::value_type>::value>::type >
658+
VARIANT_INLINE variant<Types...>& operator=(T&& rhs)
659+
// not that we check is_nothrow_constructible<T>, not is_nothrow_move_assignable<T>,
660+
// since we construct a temporary
661+
noexcept(std::is_nothrow_constructible<typename Traits::target_type, T&&>::value
662+
&& std::is_nothrow_move_assignable<variant<Types...>>::value)
655663
{
656664
variant<Types...> temp(std::forward<T>(rhs));
657665
move_assign(std::move(temp));

‎test/t/nothrow_move.cpp

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include <typeinfo>
2+
#include <utility>
3+
4+
#include <mapbox/variant.hpp>
5+
6+
using namespace mapbox;
7+
8+
namespace test {
9+
10+
struct t_noexcept_true_1 {
11+
t_noexcept_true_1(t_noexcept_true_1&&) noexcept = default;
12+
t_noexcept_true_1& operator=(t_noexcept_true_1&&) noexcept = default;
13+
};
14+
15+
struct t_noexcept_true_2 {
16+
t_noexcept_true_2(t_noexcept_true_2&&) noexcept = default;
17+
t_noexcept_true_2& operator=(t_noexcept_true_2&&) noexcept = default;
18+
};
19+
20+
struct t_noexcept_false_1 {
21+
t_noexcept_false_1(t_noexcept_false_1&&) noexcept(false) {}
22+
t_noexcept_false_1& operator=(t_noexcept_false_1&&) noexcept(false) { return *this; }
23+
};
24+
25+
using should_be_no_throw_copyable = util::variant<t_noexcept_true_1, t_noexcept_true_2>;
26+
static_assert(std::is_nothrow_move_assignable<should_be_no_throw_copyable>::value,
27+
"variants with no-throw move assignable types should be "
28+
"no-throw move nothrow assignable");
29+
30+
using should_be_no_throw_assignable = util::variant<t_noexcept_true_1, t_noexcept_true_2>;
31+
static_assert(std::is_nothrow_move_constructible<should_be_no_throw_assignable>::value,
32+
"variants with no-throw move assignable types should be "
33+
"no-throw move nothrow assignable");
34+
35+
using should_not_be_no_throw_copyable = util::variant<t_noexcept_true_1, t_noexcept_false_1>;
36+
static_assert(not std::is_nothrow_move_assignable<should_not_be_no_throw_copyable>::value,
37+
"variants with no-throw move assignable types should be "
38+
"no-throw move nothrow assignable");
39+
40+
using should_not_be_no_throw_assignable = util::variant<t_noexcept_true_1, t_noexcept_false_1>;
41+
static_assert(not std::is_nothrow_move_constructible<should_not_be_no_throw_assignable>::value,
42+
"variants with no-throw move assignable types should be "
43+
"no-throw move nothrow assignable");
44+
45+
46+
// this type cannot be nothrow converted from either of its types, even the nothrow moveable one,
47+
// because the conversion operator moves the whole variant.
48+
using convertable_test_type = util::variant<t_noexcept_true_1, t_noexcept_false_1>;
49+
50+
// this type can be nothrow converted from either of its types.
51+
using convertable_test_type_2 = util::variant<t_noexcept_true_1, t_noexcept_true_2>;
52+
53+
static_assert(not std::is_nothrow_assignable<convertable_test_type, t_noexcept_true_1>::value,
54+
"variants with noexcept(true) move constructible types should be nothrow-convertible "
55+
"from those types only IF the variant itself is nothrow_move_assignable");
56+
57+
static_assert(not std::is_nothrow_assignable<convertable_test_type, t_noexcept_false_1>::value,
58+
"variants with noexcept(false) move constructible types should not be nothrow-convertible "
59+
"from those types");
60+
61+
static_assert(std::is_nothrow_assignable<convertable_test_type_2, t_noexcept_true_2>::value,
62+
"variants with noexcept(true) move constructible types should be nothrow-convertible "
63+
"from those types only IF the variant itself is nothrow_move_assignable");
64+
65+
66+
} // namespace test

0 commit comments

Comments
 (0)
Please sign in to comment.