From e45b11d38b7c1f7ce7370e718532ccc95e5493a3 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Fri, 2 Oct 2026 15:20:18 +0900 Subject: [PATCH] Correctly direct-non-list-initialize --- include/iris/rvariant/rvariant.hpp | 50 +++++++++++++----- test/rvariant/rvariant.cpp | 84 ++++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 16 deletions(-) diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index 3d8af45..b857553 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -472,15 +472,27 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN (sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_move_assignable_v) || is_recursive_wrapper_v ) { - T tmp{std::forward(args)...}; // may throw - static_assert(noexcept(t_old_i = std::move(tmp))); - t_old_i = std::move(tmp); + static_assert(noexcept(t_old_i = std::declval())); + if constexpr (sizeof...(Args) == 0) { + T tmp = T(); // may throw + t_old_i = std::move(tmp); + } else { + T tmp(std::forward(args)...); // may throw + t_old_i = std::move(tmp); + } + } else if constexpr ( sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_copy_assignable_v ) { // strange type... - T const tmp{std::forward(args)...}; // may throw - static_assert(noexcept(t_old_i = tmp)); - t_old_i = tmp; + static_assert(noexcept(t_old_i = std::declval())); + if constexpr (sizeof...(Args) == 0) { + T const tmp = T(); // may throw + t_old_i = tmp; + } else { + T const tmp(std::forward(args)...); // may throw + t_old_i = tmp; + } + } else { static_assert(!never_valueless); t_old_i.~T_old_i(); @@ -495,19 +507,33 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN (sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_move_constructible_v) || is_recursive_wrapper_v ) { - T tmp{std::forward(args)...}; // may throw - t_old_i.~T_old_i(); static_assert(std::is_nothrow_constructible_v, T&&>); - std::construct_at(&this->storage_, std::in_place_index, std::move(tmp)); // never throws + if constexpr (sizeof...(Args) == 0) { + T tmp = T(); // may throw + t_old_i.~T_old_i(); + std::construct_at(&this->storage_, std::in_place_index, std::move(tmp)); // never throws + } else { + T tmp(std::forward(args)...); // may throw + t_old_i.~T_old_i(); + std::construct_at(&this->storage_, std::in_place_index, std::move(tmp)); // never throws + } this->index_ = I; + } else if constexpr ( sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_copy_constructible_v ) { // strange type... - T const tmp{std::forward(args)...}; // may throw - t_old_i.~T_old_i(); static_assert(std::is_nothrow_constructible_v, T const&>); - std::construct_at(&this->storage_, std::in_place_index, tmp); // never throws + if constexpr (sizeof...(Args) == 0) { + T const tmp = T(); // may throw + t_old_i.~T_old_i(); + std::construct_at(&this->storage_, std::in_place_index, tmp); // never throws + } else { + T const tmp(std::forward(args)...); // may throw + t_old_i.~T_old_i(); + std::construct_at(&this->storage_, std::in_place_index, tmp); // never throws + } this->index_ = I; + } else { static_assert(!never_valueless); t_old_i.~T_old_i(); diff --git a/test/rvariant/rvariant.cpp b/test/rvariant/rvariant.cpp index 3803f82..615a537 100644 --- a/test/rvariant/rvariant.cpp +++ b/test/rvariant/rvariant.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1195,11 +1196,11 @@ TEST_CASE("emplace") using V = iris::rvariant; { V v(std::in_place_type); - v.emplace(); // type-changing & no args; test T{} + v.emplace(); // type-changing & no args; test T() } { V v(std::in_place_type); - v.emplace(); // non-type-changing & no args; test T{} + v.emplace(); // non-type-changing & no args; test T() } } { @@ -1214,15 +1215,90 @@ TEST_CASE("emplace") using V = iris::rvariant; { V v(std::in_place_type); - v.emplace(); // type-changing & no args; test T{} + v.emplace(); // type-changing & no args; test T() } { V v(std::in_place_type); - v.emplace(); // non-type-changing & no args; test T{} + v.emplace(); // non-type-changing & no args; test T() } } // NOLINTEND(modernize-use-equals-default) } + { + // NOLINTBEGIN(modernize-use-equals-default) + // The contained value is direct-non-list-initialized ([variant.mod]): an initializer_list + // constructor is not preferred, and a narrowing into the parameter of a constructor is not + // ill-formed. `S(int)` is not `noexcept` so that the never-valueless paths are taken. + { + struct S + { + S(int value) noexcept(false) : value(value) {} // potentially-throwing + S(std::initializer_list) noexcept(false) : from_list(true) {} // potentially-throwing + int value = 0; + bool from_list = false; + }; + using V = iris::rvariant; + long long wide = 42; + + V v(std::in_place_type); + v.emplace(1); // type-changing; test T(args...) + CHECK(!iris::get(v).from_list); + CHECK(iris::get(v).value == 1); + v.emplace(2); // non-type-changing; test T(args...) + CHECK(!iris::get(v).from_list); + CHECK(iris::get(v).value == 2); + + V w(std::in_place_type); +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4244) +#endif + w.emplace(wide); // type-changing; narrowing into `S(int)` +#ifdef _MSC_VER +# pragma warning(pop) +#endif + CHECK(iris::get(w).value == 42); + w.emplace(wide); // non-type-changing; narrowing into `S(int)` + CHECK(iris::get(w).value == 42); + } + { + struct StrangeS + { + StrangeS(int value) noexcept(false) : value(value) {} // potentially-throwing + StrangeS(std::initializer_list) noexcept(false) : from_list(true) {} // potentially-throwing + StrangeS(StrangeS&&) noexcept {} // not trivial + StrangeS(StrangeS const&) = default; // trivial + StrangeS& operator=(StrangeS&&) noexcept { return *this; } // not trivial + StrangeS& operator=(StrangeS const&) = default; // trivial + int value = 0; + bool from_list = false; + }; + using V = iris::rvariant; + long long wide = 42; + + V v(std::in_place_type); + v.emplace(1); // type-changing; test T(args...) + CHECK(!iris::get(v).from_list); + CHECK(iris::get(v).value == 1); + v.emplace(2); // non-type-changing; test T(args...) + CHECK(!iris::get(v).from_list); + CHECK(iris::get(v).value == 2); + + V w(std::in_place_type); +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4244) +#endif + w.emplace(wide); // type-changing; narrowing into `StrangeS(int)` +#ifdef _MSC_VER +# pragma warning(pop) +#endif + CHECK(iris::get(w).value == 42); + w.emplace(wide); // non-type-changing; narrowing into `StrangeS(int)` + CHECK(iris::get(w).value == 42); + } + // NOLINTEND(modernize-use-equals-default) + } // ReSharper disable CppStaticAssertFailure