From f4f47ca317fddd29bc708902a95f080fe35a9680 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 3 Mar 2026 08:57:28 +0900 Subject: [PATCH 01/10] Refactor workaround for iris-cpp/iris#42 --- include/iris/hash_fwd.hpp | 2 +- include/iris/requirements.hpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/iris/hash_fwd.hpp b/include/iris/hash_fwd.hpp index c0eb75b..657cc91 100644 --- a/include/iris/hash_fwd.hpp +++ b/include/iris/hash_fwd.hpp @@ -5,7 +5,7 @@ #include -#if __cplusplus <= 202302L || \ +#if !defined(_MSC_VER) && __cplusplus <= 202302L || \ (defined(_MSVC_STL_UPDATE) && _MSVC_STL_UPDATE < 202504L) || \ (defined(__GLIBCXX__) && __GLIBCXX__ < 20241201) || \ (defined(_LIBCPP_STD_VER) && _LIBCPP_STD_VER < 26) diff --git a/include/iris/requirements.hpp b/include/iris/requirements.hpp index ad6bfd0..724e0e4 100644 --- a/include/iris/requirements.hpp +++ b/include/iris/requirements.hpp @@ -8,7 +8,6 @@ #include #include -#include #include #include From 95126533b136743222a48f7b5cb25d0c97b27c7a Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 3 Mar 2026 08:58:06 +0900 Subject: [PATCH 02/10] Split `recursive_wrapper` and `recursive_wrapper_alloca` --- .../iris/rvariant/detail/recursive_traits.hpp | 19 +- include/iris/rvariant/detail/rvariant_fwd.hpp | 7 +- .../rvariant/detail/variant_requirements.hpp | 130 -------- include/iris/rvariant/recursive_wrapper.hpp | 295 ++++++++++++++++-- .../iris/rvariant/recursive_wrapper_pmr.hpp | 4 +- include/iris/rvariant/rvariant.hpp | 99 +++++- include/iris/rvariant/variant_helper.hpp | 34 +- test/rvariant/recursive_wrapper.cpp | 6 +- 8 files changed, 428 insertions(+), 166 deletions(-) delete mode 100644 include/iris/rvariant/detail/variant_requirements.hpp diff --git a/include/iris/rvariant/detail/recursive_traits.hpp b/include/iris/rvariant/detail/recursive_traits.hpp index a4d7c1b..398aad8 100644 --- a/include/iris/rvariant/detail/recursive_traits.hpp +++ b/include/iris/rvariant/detail/recursive_traits.hpp @@ -1,4 +1,4 @@ -#ifndef IRIS_RVARIANT_DETAIL_RECURSIVE_TRAITS_HPP +#ifndef IRIS_RVARIANT_DETAIL_RECURSIVE_TRAITS_HPP #define IRIS_RVARIANT_DETAIL_RECURSIVE_TRAITS_HPP // SPDX-License-Identifier: MIT @@ -10,6 +10,12 @@ namespace iris::detail { +template +constexpr bool is_recursive_wrapper_like_v = + is_ttp_specialization_of_v || + is_ttp_specialization_of_v; + + template struct select_maybe_wrapped_impl; @@ -20,10 +26,17 @@ struct select_maybe_wrapped_impl static constexpr std::size_t index = I; }; +template +struct select_maybe_wrapped_impl, Rest...> +{ + using type = recursive_wrapper; + static constexpr std::size_t index = I; +}; + template -struct select_maybe_wrapped_impl, Rest...> +struct select_maybe_wrapped_impl, Rest...> { - using type = recursive_wrapper; + using type = recursive_wrapper_alloca; static constexpr std::size_t index = I; }; diff --git a/include/iris/rvariant/detail/rvariant_fwd.hpp b/include/iris/rvariant/detail/rvariant_fwd.hpp index ad0dc53..eae8571 100644 --- a/include/iris/rvariant/detail/rvariant_fwd.hpp +++ b/include/iris/rvariant/detail/rvariant_fwd.hpp @@ -1,4 +1,4 @@ -#ifndef IRIS_RVARIANT_DETAIL_RVARIANT_FWD_HPP +#ifndef IRIS_RVARIANT_DETAIL_RVARIANT_FWD_HPP #define IRIS_RVARIANT_DETAIL_RVARIANT_FWD_HPP // SPDX-License-Identifier: MIT @@ -20,9 +20,12 @@ namespace iris { template class rvariant; -template +template class recursive_wrapper; +template +class recursive_wrapper_alloca; + namespace detail { diff --git a/include/iris/rvariant/detail/variant_requirements.hpp b/include/iris/rvariant/detail/variant_requirements.hpp deleted file mode 100644 index 5a51241..0000000 --- a/include/iris/rvariant/detail/variant_requirements.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef IRIS_RVARIANT_DETAIL_VARIANT_REQUIREMENTS_HPP -#define IRIS_RVARIANT_DETAIL_VARIANT_REQUIREMENTS_HPP - -// SPDX-License-Identifier: MIT - -#include - -#include - -namespace iris::detail { - -template -struct check_recursive_wrapper_duplicate_impl : std::true_type {}; - -template -struct check_recursive_wrapper_duplicate_impl, T> - : std::false_type -{ - // ReSharper disable once CppStaticAssertFailure - static_assert( - false, - "rvariant cannot contain both `T` and `recursive_wrapper` " - "([rvariant.rvariant.general])." - ); -}; - -template -struct check_recursive_wrapper_duplicate_impl> - : std::false_type -{ - // ReSharper disable once CppStaticAssertFailure - static_assert( - false, - "rvariant cannot contain both `T` and `recursive_wrapper` " - "([rvariant.rvariant.general])." - ); -}; - -template - requires (!std::is_same_v) -struct check_recursive_wrapper_duplicate_impl, recursive_wrapper> - : std::false_type -{ - // ReSharper disable once CppStaticAssertFailure - static_assert( - false, - "rvariant cannot contain multiple different allocator specializations of " - "`recursive_wrapper` for the same `T` ([rvariant.rvariant.general])." - ); -}; - -template -struct check_recursive_wrapper_duplicate : std::true_type {}; - -template requires (sizeof...(Ts) > 0) -struct check_recursive_wrapper_duplicate - : std::conjunction...> -{}; - -template -struct non_wrapped_exactly_once : exactly_once -{ - static_assert( - !is_ttp_specialization_of_v, - "Constructing a `recursive_wrapper` alternative with its full type as the tag is " - "prohibited to avoid confusion; just specify `T` instead." - ); -}; - -template -constexpr bool non_wrapped_exactly_once_v = non_wrapped_exactly_once::value; - - -template -struct exactly_once_index -{ - static_assert(exactly_once_v, "T or recursive_wrapper must occur exactly once in Ts..."); - static constexpr std::size_t value = find_index_v; -}; - -template -inline constexpr std::size_t exactly_once_index_v = exactly_once_index::value; - - -template -struct variant_copy_assignable : std::conjunction, std::is_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(std::is_lvalue_reference_v); -}; - -template -struct variant_nothrow_copy_assignable : std::conjunction, std::is_nothrow_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(std::is_lvalue_reference_v); - static_assert(variant_copy_assignable::value); -}; - -template -struct variant_move_assignable : std::conjunction, std::is_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(std::is_rvalue_reference_v); -}; - -template -struct variant_nothrow_move_assignable : std::conjunction, std::is_nothrow_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(std::is_rvalue_reference_v); - static_assert(variant_move_assignable::value); -}; - -template -struct variant_assignable : std::conjunction, std::is_assignable> -{ - static_assert(!std::is_reference_v); -}; - -template -struct variant_nothrow_assignable : std::conjunction, std::is_nothrow_assignable> -{ - static_assert(!std::is_reference_v); - static_assert(variant_assignable::value); -}; - -} // iris::detail - -#endif diff --git a/include/iris/rvariant/recursive_wrapper.hpp b/include/iris/rvariant/recursive_wrapper.hpp index f29f771..3eb1520 100644 --- a/include/iris/rvariant/recursive_wrapper.hpp +++ b/include/iris/rvariant/recursive_wrapper.hpp @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -14,17 +13,17 @@ namespace iris { -template> +// recursive_wrapper (fixed to `std::allocator`) +// This class covers 99.99% of recursive-variant use cases. +template class recursive_wrapper - : private iris::detail::indirect_base + : private iris::detail::indirect_base> { - static_assert(std::is_object_v); - static_assert(!std::is_array_v); - static_assert(!std::is_same_v); - static_assert(!is_ttp_specialization_of_v); - static_assert(!std::is_const_v && !std::is_volatile_v); - static_assert(std::is_same_v::value_type>); + // Note: this implementation is copied from `recursive_wrapper_alloca` below. + // If any changes are required, modify `recursive_wrapper_alloca` first. + // See https://github.com/iris-cpp/iris/issues/43 for rationale + using Allocator = std::allocator; using base_type = iris::detail::indirect_base; public: @@ -158,16 +157,186 @@ class recursive_wrapper } }; +// "Allocator-aware" recursive_wrapper +template> +class recursive_wrapper_alloca + : private iris::detail::indirect_base +{ + using base_type = iris::detail::indirect_base; + +public: + using typename base_type::allocator_type; + using typename base_type::const_pointer; + using typename base_type::pointer; + using typename base_type::value_type; + + // Don't do this; it breaks third-party analyzer like ReSharper on MSVC + //using base_type::base_type; + + constexpr /* not explicit */ recursive_wrapper_alloca() + requires std::is_default_constructible_v + = default; + + // Required for combination with defaulted assignment operators + constexpr recursive_wrapper_alloca(recursive_wrapper_alloca const&) = default; + constexpr recursive_wrapper_alloca(recursive_wrapper_alloca&&) noexcept = default; + + constexpr explicit recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a) + noexcept(noexcept(base_type(std::allocator_arg, a))) + : base_type(std::allocator_arg, a) + {} + + constexpr recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, recursive_wrapper_alloca const& other) + noexcept(noexcept(base_type(std::allocator_arg, a, other))) + : base_type(std::allocator_arg, a, other) + {} + + constexpr recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, recursive_wrapper_alloca&& other) + noexcept(noexcept(base_type(std::allocator_arg, a, std::move(other)))) + : base_type(std::allocator_arg, a, std::move(other)) + {} + + // Converting constructor + template + requires + (!std::is_same_v, recursive_wrapper_alloca>) && + (!std::is_same_v, std::in_place_t>) && + std::is_default_constructible_v && + //std::is_constructible_v // UNIMPLEMENTABLE for recursive types; instantiates infinitely + std::is_convertible_v + constexpr /* not explicit */ recursive_wrapper_alloca(U&& u) + // This overload is never `noexcept` as it always allocates. + // Note that conditionally enabling `noexcept` will lead to + // recursive instantiation in some situations. + : base_type(std::forward(u)) + {} + + template + requires + (!std::is_same_v, recursive_wrapper_alloca>) && + (!std::is_same_v, std::in_place_t>) && + std::is_constructible_v + constexpr explicit recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, U&& u) + noexcept(noexcept(base_type(std::allocator_arg, a, std::forward(u)))) + : base_type(std::allocator_arg, a, std::forward(u)) + {} + + template + requires + std::is_constructible_v && + std::is_default_constructible_v + constexpr explicit recursive_wrapper_alloca(std::in_place_t, Us&&... us) + noexcept(noexcept(base_type(std::in_place, std::forward(us)...))) + : base_type(std::in_place, std::forward(us)...) + {} + + template + requires std::is_constructible_v + constexpr explicit recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, std::in_place_t, Us&&... us) + noexcept(noexcept(base_type(std::allocator_arg, a, std::in_place, std::forward(us)...))) + : base_type(std::allocator_arg, a, std::in_place, std::forward(us)...) + {} + + template + requires + std::is_constructible_v&, Us...> && + std::is_default_constructible_v + constexpr explicit recursive_wrapper_alloca(std::in_place_t, std::initializer_list il, Us&&... us) + noexcept(noexcept(base_type(std::in_place, il, std::forward(us)...))) + : base_type(std::in_place, il, std::forward(us)...) + {} + + template + requires std::is_constructible_v&, Us...> + constexpr explicit recursive_wrapper_alloca(std::allocator_arg_t, Allocator const& a, std::in_place_t, std::initializer_list il, Us&&... us) + noexcept(noexcept(base_type(std::allocator_arg, a, std::in_place, il, std::forward(us)...))) + : base_type(std::allocator_arg, a, std::in_place, il, std::forward(us)...) + {} + + constexpr ~recursive_wrapper_alloca() = default; + + // Don't do this; it will lead to surprising result that + // MSVC attempts to instantiate move assignment operator of *rvariant* + // when a user just *defines* a struct that contains a rvariant. + // I don't know why it happens, but MSVC is certainly doing something weird + // so that it eagerly instantiates unrelated member functions. + //using base_type::operator=; + + constexpr recursive_wrapper_alloca& operator=(recursive_wrapper_alloca const&) = default; + + constexpr recursive_wrapper_alloca& operator=(recursive_wrapper_alloca&&) noexcept( + std::allocator_traits::propagate_on_container_move_assignment::value || + std::allocator_traits::is_always_equal::value + ) = default; + + // This is required for proper delegation; otherwise constructor will be called + template + requires + (!std::is_same_v, recursive_wrapper_alloca>) && + std::is_constructible_v && + std::is_assignable_v + constexpr recursive_wrapper_alloca& operator=(U&& u) + { + base_type::operator=(std::forward(u)); + return *this; + } + + using base_type::operator*; + using base_type::operator->; + using base_type::valueless_after_move; + using base_type::get_allocator; + + using base_type::swap; + + friend constexpr void swap(recursive_wrapper_alloca& lhs, recursive_wrapper_alloca& rhs) + noexcept(noexcept(lhs.swap(rhs))) + { + lhs.swap(rhs); + } +}; + +// --------------------------------------------------- +// recursive_wrapper + template recursive_wrapper(Value) -> recursive_wrapper; +template +[[nodiscard]] constexpr bool operator==(recursive_wrapper const& lhs, recursive_wrapper const& rhs) + noexcept(noexcept(*lhs == *rhs)) +{ + if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] { + return lhs.valueless_after_move() == rhs.valueless_after_move(); + } else [[likely]] { + return *lhs == *rhs; + } +} + +template +[[nodiscard]] constexpr bool operator==(recursive_wrapper const& lhs, U const& rhs) + noexcept(noexcept(*lhs == rhs)) +{ + if (lhs.valueless_after_move()) [[unlikely]] { + return false; + } else [[likely]] { + return *lhs == rhs; + } +} + +// --------------------------------------------------- +// recursive_wrapper_alloca + +template +recursive_wrapper_alloca(Value) + -> recursive_wrapper_alloca; + template -recursive_wrapper(std::allocator_arg_t, Allocator, Value) - -> recursive_wrapper::template rebind_alloc>; +recursive_wrapper_alloca(std::allocator_arg_t, Allocator, Value) + -> recursive_wrapper_alloca::template rebind_alloc>; template -[[nodiscard]] constexpr bool operator==(recursive_wrapper const& lhs, recursive_wrapper const& rhs) +[[nodiscard]] constexpr bool operator==(recursive_wrapper_alloca const& lhs, recursive_wrapper_alloca const& rhs) noexcept(noexcept(*lhs == *rhs)) { if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] { @@ -178,7 +347,7 @@ template } template -[[nodiscard]] constexpr bool operator==(recursive_wrapper const& lhs, U const& rhs) +[[nodiscard]] constexpr bool operator==(recursive_wrapper_alloca const& lhs, U const& rhs) noexcept(noexcept(*lhs == rhs)) { if (lhs.valueless_after_move()) [[unlikely]] { @@ -193,8 +362,36 @@ namespace detail { // These cannot be overloaded with the same function name, as it // breaks MSVC's overload resolution on recursive types (possibly bug) +// --------------------------------------------------- +// recursive_wrapper + +template +[[nodiscard]] constexpr auto rw_three_way_impl_00(recursive_wrapper const& lhs, recursive_wrapper const& rhs) + -> cmp::synth_three_way_result +{ + if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] { + return !lhs.valueless_after_move() <=> !rhs.valueless_after_move(); + } else [[likely]] { + return cmp::synth_three_way{}(*lhs, *rhs); + } +} + +template +[[nodiscard]] constexpr auto rw_three_way_impl_01(recursive_wrapper const& lhs, U const& rhs) + -> cmp::synth_three_way_result +{ + if (lhs.valueless_after_move()) [[unlikely]] { + return std::strong_ordering::less; + } else [[likely]] { + return cmp::synth_three_way{}(*lhs, rhs); + } +} + +// --------------------------------------------------- +// recursive_wrapper_alloca + template -[[nodiscard]] constexpr auto rw_three_way_impl_00(recursive_wrapper const& lhs, recursive_wrapper const& rhs) +[[nodiscard]] constexpr auto rw_three_way_impl_00(recursive_wrapper_alloca const& lhs, recursive_wrapper_alloca const& rhs) -> cmp::synth_three_way_result { if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] { @@ -205,7 +402,7 @@ template } template -[[nodiscard]] constexpr auto rw_three_way_impl_01(recursive_wrapper const& lhs, U const& rhs) +[[nodiscard]] constexpr auto rw_three_way_impl_01(recursive_wrapper_alloca const& lhs, U const& rhs) -> cmp::synth_three_way_result { if (lhs.valueless_after_move()) [[unlikely]] { @@ -217,15 +414,36 @@ template } // detail + +// --------------------------------------------------- +// recursive_wrapper + +template +[[nodiscard]] constexpr auto operator<=>(recursive_wrapper const& lhs, recursive_wrapper const& rhs) + // no explicit return type +{ + return detail::rw_three_way_impl_00(lhs, rhs); +} + +template +[[nodiscard]] constexpr auto operator<=>(recursive_wrapper const& lhs, U const& rhs) + // no explicit return type +{ + return detail::rw_three_way_impl_01(lhs, rhs); +} + +// --------------------------------------------------- +// recursive_wrapper_alloca + template -[[nodiscard]] constexpr auto operator<=>(recursive_wrapper const& lhs, recursive_wrapper const& rhs) +[[nodiscard]] constexpr auto operator<=>(recursive_wrapper_alloca const& lhs, recursive_wrapper_alloca const& rhs) // no explicit return type { return detail::rw_three_way_impl_00(lhs, rhs); } template -[[nodiscard]] constexpr auto operator<=>(recursive_wrapper const& lhs, U const& rhs) +[[nodiscard]] constexpr auto operator<=>(recursive_wrapper_alloca const& lhs, U const& rhs) // no explicit return type { return detail::rw_three_way_impl_01(lhs, rhs); @@ -235,11 +453,32 @@ template namespace std { +// --------------------------------------------------- +// recursive_wrapper + +template + requires ::iris::is_hash_enabled_v +struct hash<::iris::recursive_wrapper> // NOLINT(cert-dcl58-cpp) +{ + [[nodiscard]] static size_t operator()(::iris::recursive_wrapper const& obj) + noexcept(::iris::is_nothrow_hashable_v) + { + if (obj.valueless_after_move()) [[unlikely]] { + return 0xbaddeadbeefuz; + } else [[likely]] { + return std::hash{}(*obj); + } + } +}; + +// --------------------------------------------------- +// recursive_wrapper_alloca + template requires ::iris::is_hash_enabled_v -struct hash<::iris::recursive_wrapper> // NOLINT(cert-dcl58-cpp) +struct hash<::iris::recursive_wrapper_alloca> // NOLINT(cert-dcl58-cpp) { - [[nodiscard]] static size_t operator()(::iris::recursive_wrapper const& obj) + [[nodiscard]] static size_t operator()(::iris::recursive_wrapper_alloca const& obj) noexcept(::iris::is_nothrow_hashable_v) { if (obj.valueless_after_move()) [[unlikely]] { @@ -255,12 +494,26 @@ struct hash<::iris::recursive_wrapper> // NOLINT(cert-dcl58-cpp) namespace iris { +// --------------------------------------------------- +// recursive_wrapper + +template + requires is_hash_enabled_v +[[nodiscard]] std::size_t hash_value(recursive_wrapper const& obj) + noexcept(is_nothrow_hashable_v) +{ + return std::hash>{}(obj); +} + +// --------------------------------------------------- +// recursive_wrapper_alloca + template requires is_hash_enabled_v -[[nodiscard]] std::size_t hash_value(recursive_wrapper const& obj) +[[nodiscard]] std::size_t hash_value(recursive_wrapper_alloca const& obj) noexcept(is_nothrow_hashable_v) { - return std::hash>{}(obj); + return std::hash>{}(obj); } } // iris diff --git a/include/iris/rvariant/recursive_wrapper_pmr.hpp b/include/iris/rvariant/recursive_wrapper_pmr.hpp index 0e5e020..853fd58 100644 --- a/include/iris/rvariant/recursive_wrapper_pmr.hpp +++ b/include/iris/rvariant/recursive_wrapper_pmr.hpp @@ -1,4 +1,4 @@ -#ifndef IRIS_RVARIANT_RECURSIVE_WRAPPER_PMR_HPP +#ifndef IRIS_RVARIANT_RECURSIVE_WRAPPER_PMR_HPP #define IRIS_RVARIANT_RECURSIVE_WRAPPER_PMR_HPP // SPDX-License-Identifier: MIT @@ -10,7 +10,7 @@ namespace iris::pmr { template -using recursive_wrapper = iris::recursive_wrapper>; +using recursive_wrapper = iris::recursive_wrapper_alloca>; } // iris::pmr diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index 19f0a95..fa9eb5a 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -4,7 +4,6 @@ // SPDX-License-Identifier: MIT #include -#include #include #include #include @@ -18,6 +17,7 @@ #include #include +#include #include #include #include @@ -32,6 +32,103 @@ namespace iris { namespace detail { +template +struct check_recursive_wrapper_duplicate_impl : std::true_type {}; + +template + requires + (!std::same_as) && + (is_recursive_wrapper_like_v || is_recursive_wrapper_like_v) && + std::same_as, unwrap_recursive_type> +struct check_recursive_wrapper_duplicate_impl + : std::false_type +{ + // ReSharper disable once CppStaticAssertFailure + static_assert( + false, + "rvariant cannot contain both `T` and `recursive_wrapper` of `T` " + "([rvariant.rvariant.general])." + ); +}; + +template +struct check_recursive_wrapper_duplicate : std::true_type {}; + +template requires (sizeof...(Ts) > 0) +struct check_recursive_wrapper_duplicate + : std::conjunction...> +{}; + +template +struct non_wrapped_exactly_once : exactly_once +{ + static_assert( + !is_ttp_specialization_of_v && + !is_ttp_specialization_of_v, + "Constructing a `recursive_wrapper` alternative with its full type as the tag is " + "prohibited to avoid confusion; just specify `T` instead." + ); +}; + +template +constexpr bool non_wrapped_exactly_once_v = non_wrapped_exactly_once::value; + + +template +struct exactly_once_index +{ + static_assert(exactly_once_v, "T or recursive_wrapper must occur exactly once in Ts..."); + static constexpr std::size_t value = find_index_v; +}; + +template +inline constexpr std::size_t exactly_once_index_v = exactly_once_index::value; + + +template +struct variant_copy_assignable : std::conjunction, std::is_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(std::is_lvalue_reference_v); +}; + +template +struct variant_nothrow_copy_assignable : std::conjunction, std::is_nothrow_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(std::is_lvalue_reference_v); + static_assert(variant_copy_assignable::value); +}; + +template +struct variant_move_assignable : std::conjunction, std::is_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(std::is_rvalue_reference_v); +}; + +template +struct variant_nothrow_move_assignable : std::conjunction, std::is_nothrow_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(std::is_rvalue_reference_v); + static_assert(variant_move_assignable::value); +}; + +template +struct variant_assignable : std::conjunction, std::is_assignable> +{ + static_assert(!std::is_reference_v); +}; + +template +struct variant_nothrow_assignable : std::conjunction, std::is_nothrow_assignable> +{ + static_assert(!std::is_reference_v); + static_assert(variant_assignable::value); +}; + + template struct relops_visitor; diff --git a/include/iris/rvariant/variant_helper.hpp b/include/iris/rvariant/variant_helper.hpp index 840dc51..9650e0f 100644 --- a/include/iris/rvariant/variant_helper.hpp +++ b/include/iris/rvariant/variant_helper.hpp @@ -75,8 +75,14 @@ struct unwrap_recursive_type_impl using type = T; }; +template +struct unwrap_recursive_type_impl> +{ + using type = T; +}; + template -struct unwrap_recursive_type_impl> +struct unwrap_recursive_type_impl> { using type = T; }; @@ -132,20 +138,40 @@ template struct forward_maybe_wrapped_impl; // [rvariant.rvariant.general]: different allocators are not allowed // recursive_wrapper val = recursive_wrapper{42}; +template +struct forward_maybe_wrapped_impl, recursive_wrapper> +{ + template + [[nodiscard]] static constexpr auto&& apply(Wrapped&& wrapped IRIS_LIFETIMEBOUND) noexcept + { + static_assert(std::is_same_v, recursive_wrapper>); + return std::forward(wrapped); + } +}; template -struct forward_maybe_wrapped_impl, recursive_wrapper> +struct forward_maybe_wrapped_impl, recursive_wrapper_alloca> { template [[nodiscard]] static constexpr auto&& apply(Wrapped&& wrapped IRIS_LIFETIMEBOUND) noexcept { - static_assert(std::is_same_v, recursive_wrapper>); + static_assert(std::is_same_v, recursive_wrapper_alloca>); return std::forward(wrapped); } }; // recursive_wrapper val = 42; +template +struct forward_maybe_wrapped_impl, T> +{ + template + [[nodiscard]] static constexpr auto&& apply(Value&& value IRIS_LIFETIMEBOUND) noexcept + { + static_assert(std::is_same_v, T>); + return std::forward(value); + } +}; template -struct forward_maybe_wrapped_impl, T> +struct forward_maybe_wrapped_impl, T> { template [[nodiscard]] static constexpr auto&& apply(Value&& value IRIS_LIFETIMEBOUND) noexcept diff --git a/test/rvariant/recursive_wrapper.cpp b/test/rvariant/recursive_wrapper.cpp index b3cfb76..4b8506c 100644 --- a/test/rvariant/recursive_wrapper.cpp +++ b/test/rvariant/recursive_wrapper.cpp @@ -30,7 +30,7 @@ TEST_CASE("relational operators", "[wrapper]") CHECK((a <=> b) == std::strong_ordering::greater); } { - iris::recursive_wrapper a(33); + iris::recursive_wrapper a(33); int b = 4; CHECK(a == a); @@ -62,8 +62,8 @@ TEST_CASE("relational operators", "[wrapper]") { struct MyAllocator : std::allocator {}; - iris::recursive_wrapper a(33); - iris::recursive_wrapper b(4); + iris::recursive_wrapper_alloca a(33); + iris::recursive_wrapper_alloca b(4); CHECK(a == a); CHECK(a != b); From 3233c28cb3e0f48bc6bc96207ae1d234d6f39af4 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:22:51 +0900 Subject: [PATCH 03/10] Remove allocator-related feature from plain `recursive_wrapper` --- include/iris/rvariant/recursive_wrapper.hpp | 64 +++------------------ test/rvariant/rvariant.cpp | 2 +- 2 files changed, 9 insertions(+), 57 deletions(-) diff --git a/include/iris/rvariant/recursive_wrapper.hpp b/include/iris/rvariant/recursive_wrapper.hpp index 3eb1520..03a8e03 100644 --- a/include/iris/rvariant/recursive_wrapper.hpp +++ b/include/iris/rvariant/recursive_wrapper.hpp @@ -17,14 +17,13 @@ namespace iris { // This class covers 99.99% of recursive-variant use cases. template class recursive_wrapper - : private iris::detail::indirect_base> + : private detail::indirect_base> { // Note: this implementation is copied from `recursive_wrapper_alloca` below. // If any changes are required, modify `recursive_wrapper_alloca` first. // See https://github.com/iris-cpp/iris/issues/43 for rationale - using Allocator = std::allocator; - using base_type = iris::detail::indirect_base; + using base_type = detail::indirect_base>; public: using typename base_type::allocator_type; @@ -35,35 +34,17 @@ class recursive_wrapper // Don't do this; it breaks third-party analyzer like ReSharper on MSVC //using base_type::base_type; - constexpr /* not explicit */ recursive_wrapper() - requires std::is_default_constructible_v - = default; + constexpr /* not explicit */ recursive_wrapper() = default; // Required for combination with defaulted assignment operators constexpr recursive_wrapper(recursive_wrapper const&) = default; constexpr recursive_wrapper(recursive_wrapper&&) noexcept = default; - constexpr explicit recursive_wrapper(std::allocator_arg_t, Allocator const& a) - noexcept(noexcept(base_type(std::allocator_arg, a))) - : base_type(std::allocator_arg, a) - {} - - constexpr recursive_wrapper(std::allocator_arg_t, Allocator const& a, recursive_wrapper const& other) - noexcept(noexcept(base_type(std::allocator_arg, a, other))) - : base_type(std::allocator_arg, a, other) - {} - - constexpr recursive_wrapper(std::allocator_arg_t, Allocator const& a, recursive_wrapper&& other) - noexcept(noexcept(base_type(std::allocator_arg, a, std::move(other)))) - : base_type(std::allocator_arg, a, std::move(other)) - {} - // Converting constructor template requires (!std::is_same_v, recursive_wrapper>) && (!std::is_same_v, std::in_place_t>) && - std::is_default_constructible_v && //std::is_constructible_v // UNIMPLEMENTABLE for recursive types; instantiates infinitely std::is_convertible_v constexpr /* not explicit */ recursive_wrapper(U&& u) @@ -73,48 +54,22 @@ class recursive_wrapper : base_type(std::forward(u)) {} - template - requires - (!std::is_same_v, recursive_wrapper>) && - (!std::is_same_v, std::in_place_t>) && - std::is_constructible_v - constexpr explicit recursive_wrapper(std::allocator_arg_t, Allocator const& a, U&& u) - noexcept(noexcept(base_type(std::allocator_arg, a, std::forward(u)))) - : base_type(std::allocator_arg, a, std::forward(u)) - {} - template requires - std::is_constructible_v && - std::is_default_constructible_v + std::is_constructible_v constexpr explicit recursive_wrapper(std::in_place_t, Us&&... us) noexcept(noexcept(base_type(std::in_place, std::forward(us)...))) : base_type(std::in_place, std::forward(us)...) {} - template - requires std::is_constructible_v - constexpr explicit recursive_wrapper(std::allocator_arg_t, Allocator const& a, std::in_place_t, Us&&... us) - noexcept(noexcept(base_type(std::allocator_arg, a, std::in_place, std::forward(us)...))) - : base_type(std::allocator_arg, a, std::in_place, std::forward(us)...) - {} - template requires - std::is_constructible_v&, Us...> && - std::is_default_constructible_v + std::is_constructible_v&, Us...> constexpr explicit recursive_wrapper(std::in_place_t, std::initializer_list il, Us&&... us) noexcept(noexcept(base_type(std::in_place, il, std::forward(us)...))) : base_type(std::in_place, il, std::forward(us)...) {} - template - requires std::is_constructible_v&, Us...> - constexpr explicit recursive_wrapper(std::allocator_arg_t, Allocator const& a, std::in_place_t, std::initializer_list il, Us&&... us) - noexcept(noexcept(base_type(std::allocator_arg, a, std::in_place, il, std::forward(us)...))) - : base_type(std::allocator_arg, a, std::in_place, il, std::forward(us)...) - {} - constexpr ~recursive_wrapper() = default; // Don't do this; it will lead to surprising result that @@ -126,10 +81,7 @@ class recursive_wrapper constexpr recursive_wrapper& operator=(recursive_wrapper const&) = default; - constexpr recursive_wrapper& operator=(recursive_wrapper&&) noexcept( - std::allocator_traits::propagate_on_container_move_assignment::value || - std::allocator_traits::is_always_equal::value - ) = default; + constexpr recursive_wrapper& operator=(recursive_wrapper&&) noexcept = default; // This is required for proper delegation; otherwise constructor will be called template @@ -160,9 +112,9 @@ class recursive_wrapper // "Allocator-aware" recursive_wrapper template> class recursive_wrapper_alloca - : private iris::detail::indirect_base + : private detail::indirect_base { - using base_type = iris::detail::indirect_base; + using base_type = detail::indirect_base; public: using typename base_type::allocator_type; diff --git a/test/rvariant/rvariant.cpp b/test/rvariant/rvariant.cpp index 6f9118c..952ed0b 100644 --- a/test/rvariant/rvariant.cpp +++ b/test/rvariant/rvariant.cpp @@ -1597,7 +1597,7 @@ TEST_CASE("recursive_wrapper") // not [recursive] iris::rvariant> b(std::in_place_index<0>, 42); iris::rvariant> c(std::in_place_index<0>, std::in_place); iris::rvariant> d(std::in_place_index<0>, std::in_place, 42); - iris::rvariant> e(std::in_place_index<0>, std::allocator_arg, std::allocator{}); + iris::rvariant> e(std::in_place_index<0>, std::allocator_arg, std::allocator{}); } } From 62a4d5aff01cf9328c428638a1178e68726615f6 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:31:21 +0900 Subject: [PATCH 04/10] Remove `forward_maybe_wrapped` which seemed to be noop --- include/iris/rvariant/rvariant.hpp | 18 +++--- include/iris/rvariant/variant_helper.hpp | 71 ------------------------ 2 files changed, 9 insertions(+), 80 deletions(-) diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index fa9eb5a..40330ff 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -695,7 +695,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; using VT = maybe_wrapped::type; static_assert(std::is_same_v, unwrap_recursive_type>); - base_type::template construct_on_valueless(detail::forward_maybe_wrapped(uj)); + base_type::template construct_on_valueless(uj); } }); } @@ -718,7 +718,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END using VT = maybe_wrapped::type; static_assert(std::is_same_v, unwrap_recursive_type>); static_assert(std::is_rvalue_reference_v); - base_type::template construct_on_valueless(detail::forward_maybe_wrapped(std::move(uj))); // NOLINT(bugprone-move-forwarding-reference) + base_type::template construct_on_valueless(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) } }); } @@ -751,16 +751,16 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END { constexpr std::size_t VTi = maybe_wrapped::index; if constexpr (i == std::variant_npos) { // this is valueless, rhs holds value - base_type::template construct_on_valueless(detail::forward_maybe_wrapped(uj)); + base_type::template construct_on_valueless(uj); } else if constexpr (std::is_same_v, unwrap_recursive_type>) { - ti = detail::forward_maybe_wrapped(uj); + ti = uj; } else if constexpr (std::is_nothrow_constructible_v || !std::is_nothrow_move_constructible_v) { - base_type::template reset_construct(detail::forward_maybe_wrapped(uj)); + base_type::template reset_construct(uj); } else { - VT tmp = detail::forward_maybe_wrapped(uj); // may throw + VT tmp(uj); // may throw base_type::template reset_construct(std::move(tmp)); } }); @@ -796,13 +796,13 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END static_assert(std::is_rvalue_reference_v); constexpr std::size_t VTi = maybe_wrapped::index; if constexpr (i == std::variant_npos) { // this is valueless, rhs holds value - base_type::template construct_on_valueless(detail::forward_maybe_wrapped(std::move(uj))); // NOLINT(bugprone-move-forwarding-reference) + base_type::template construct_on_valueless(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) } else if constexpr (std::is_same_v, unwrap_recursive_type>) { - ti = detail::forward_maybe_wrapped(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) + ti = std::move(uj); // NOLINT(bugprone-move-forwarding-reference) } else { - base_type::template reset_construct(detail::forward_maybe_wrapped(std::move(uj))); // NOLINT(bugprone-move-forwarding-reference) + base_type::template reset_construct(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) } }); } diff --git a/include/iris/rvariant/variant_helper.hpp b/include/iris/rvariant/variant_helper.hpp index 9650e0f..d06fa5c 100644 --- a/include/iris/rvariant/variant_helper.hpp +++ b/include/iris/rvariant/variant_helper.hpp @@ -131,77 +131,6 @@ struct overloaded : Fs... using Fs::operator()...; }; - -namespace detail { - -template -struct forward_maybe_wrapped_impl; // [rvariant.rvariant.general]: different allocators are not allowed - -// recursive_wrapper val = recursive_wrapper{42}; -template -struct forward_maybe_wrapped_impl, recursive_wrapper> -{ - template - [[nodiscard]] static constexpr auto&& apply(Wrapped&& wrapped IRIS_LIFETIMEBOUND) noexcept - { - static_assert(std::is_same_v, recursive_wrapper>); - return std::forward(wrapped); - } -}; -template -struct forward_maybe_wrapped_impl, recursive_wrapper_alloca> -{ - template - [[nodiscard]] static constexpr auto&& apply(Wrapped&& wrapped IRIS_LIFETIMEBOUND) noexcept - { - static_assert(std::is_same_v, recursive_wrapper_alloca>); - return std::forward(wrapped); - } -}; - -// recursive_wrapper val = 42; -template -struct forward_maybe_wrapped_impl, T> -{ - template - [[nodiscard]] static constexpr auto&& apply(Value&& value IRIS_LIFETIMEBOUND) noexcept - { - static_assert(std::is_same_v, T>); - return std::forward(value); - } -}; -template -struct forward_maybe_wrapped_impl, T> -{ - template - [[nodiscard]] static constexpr auto&& apply(Value&& value IRIS_LIFETIMEBOUND) noexcept - { - static_assert(std::is_same_v, T>); - return std::forward(value); - } -}; - -// int val = 42; -template -struct forward_maybe_wrapped_impl -{ - template - [[nodiscard]] static constexpr auto&& apply(Value&& value IRIS_LIFETIMEBOUND) noexcept - { - static_assert(std::is_same_v, T>); - return std::forward(value); - } -}; - -template -[[nodiscard]] constexpr auto&& forward_maybe_wrapped(RHS&& rhs IRIS_LIFETIMEBOUND) noexcept -{ - static_assert(!std::is_reference_v && !std::is_const_v); - return forward_maybe_wrapped_impl>::apply(std::forward(rhs)); -} - -} // detail - } // iris #endif From adf36d26fd5989d4d35e1084eb4c383dfabd8517 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:51:55 +0900 Subject: [PATCH 05/10] Fix recursive wrapper detection for alloca --- .../iris/rvariant/detail/recursive_traits.hpp | 9 +------ include/iris/rvariant/recursive_wrapper.hpp | 4 +++ include/iris/rvariant/rvariant.hpp | 27 +++++++++---------- include/iris/rvariant/variant_helper.hpp | 7 ++++- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/include/iris/rvariant/detail/recursive_traits.hpp b/include/iris/rvariant/detail/recursive_traits.hpp index 398aad8..b999b18 100644 --- a/include/iris/rvariant/detail/recursive_traits.hpp +++ b/include/iris/rvariant/detail/recursive_traits.hpp @@ -6,16 +6,9 @@ // IWYU pragma: private, include #include -#include namespace iris::detail { -template -constexpr bool is_recursive_wrapper_like_v = - is_ttp_specialization_of_v || - is_ttp_specialization_of_v; - - template struct select_maybe_wrapped_impl; @@ -50,7 +43,7 @@ struct select_maybe_wrapped : select_maybe_wrapped_impl { // Precondition: either T or recursive_wrapper occurs at least once in Ts... static_assert(sizeof...(Ts) > 0); - static_assert(!is_ttp_specialization_of_v); + static_assert(!detail::is_recursive_wrapper_like_v); }; template diff --git a/include/iris/rvariant/recursive_wrapper.hpp b/include/iris/rvariant/recursive_wrapper.hpp index 03a8e03..daa0676 100644 --- a/include/iris/rvariant/recursive_wrapper.hpp +++ b/include/iris/rvariant/recursive_wrapper.hpp @@ -19,6 +19,8 @@ template class recursive_wrapper : private detail::indirect_base> { + static_assert(!detail::is_recursive_wrapper_like_v, "recursive wrapper of recursive wrapper is not allowed"); + // Note: this implementation is copied from `recursive_wrapper_alloca` below. // If any changes are required, modify `recursive_wrapper_alloca` first. // See https://github.com/iris-cpp/iris/issues/43 for rationale @@ -114,6 +116,8 @@ template> class recursive_wrapper_alloca : private detail::indirect_base { + static_assert(!detail::is_recursive_wrapper_like_v, "recursive wrapper of recursive wrapper is not allowed"); + using base_type = detail::indirect_base; public: diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index 40330ff..399c7bd 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -63,8 +63,7 @@ template struct non_wrapped_exactly_once : exactly_once { static_assert( - !is_ttp_specialization_of_v && - !is_ttp_specialization_of_v, + !detail::is_recursive_wrapper_like_v, "Constructing a `recursive_wrapper` alternative with its full type as the tag is " "prohibited to avoid confusion; just specify `T` instead." ); @@ -77,7 +76,7 @@ constexpr bool non_wrapped_exactly_once_v = non_wrapped_exactly_once::v template struct exactly_once_index { - static_assert(exactly_once_v, "T or recursive_wrapper must occur exactly once in Ts..."); + static_assert(exactly_once_v, "`T` or `recursive_wrapper` or `recursive_wrapper_alloca` must occur exactly once in Ts..."); static constexpr std::size_t value = find_index_v; }; @@ -468,7 +467,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN } else if constexpr (std::is_same_v) { // NOT type-changing if constexpr ( (sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_move_assignable_v) || - is_ttp_specialization_of_v + detail::is_recursive_wrapper_like_v ) { T tmp{std::forward(args)...}; // may throw static_assert(noexcept(t_old_i = std::move(tmp))); @@ -491,7 +490,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN } else { // type-changing if constexpr ( (sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_move_constructible_v) || - is_ttp_specialization_of_v + detail::is_recursive_wrapper_like_v ) { T tmp{std::forward(args)...}; // may throw t_old_i.~T_old_i(); @@ -1192,7 +1191,7 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END // ------------------------------------------------- template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v [[nodiscard]] constexpr bool holds_alternative(rvariant const& v) noexcept = delete; template @@ -1293,19 +1292,19 @@ get(rvariant const&& v IRIS_LIFETIMEBOUND) } template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T& get(rvariant&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T&& get(rvariant&&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T const& get(rvariant const&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T const&& get(rvariant const&&) = delete; // ------------------------------------------------- @@ -1374,19 +1373,19 @@ unsafe_get(rvariant const&& v IRIS_LIFETIMEBOUND) noexcept } template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T& unsafe_get(rvariant&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T&& unsafe_get(rvariant&&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T const& unsafe_get(rvariant const&) = delete; template - requires is_ttp_specialization_of_v + requires detail::is_recursive_wrapper_like_v constexpr T const&& unsafe_get(rvariant const&&) = delete; // --------------------------------------------- diff --git a/include/iris/rvariant/variant_helper.hpp b/include/iris/rvariant/variant_helper.hpp index d06fa5c..006ab45 100644 --- a/include/iris/rvariant/variant_helper.hpp +++ b/include/iris/rvariant/variant_helper.hpp @@ -69,6 +69,11 @@ struct variant_size> : std::integral_constant +constexpr bool is_recursive_wrapper_like_v = + is_ttp_specialization_of_v || + is_ttp_specialization_of_v; + template struct unwrap_recursive_type_impl { @@ -93,7 +98,7 @@ struct unwrap_recursive_fn [[nodiscard]] IRIS_FORCEINLINE static constexpr auto&& operator()(T&& o IRIS_LIFETIMEBOUND) noexcept { - if constexpr (is_ttp_specialization_of_v, recursive_wrapper>) { + if constexpr (is_recursive_wrapper_like_v>) { return *std::forward(o); } else { return std::forward(o); From 78e24256640965016a8ba997e3be5970c824fe12 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:28:23 +0900 Subject: [PATCH 06/10] Update does to reflect the split of `recursive_wrapper_alloca` --- doc/rvariant.adoc | 138 +++++++++++++++++++++----------------- doc/rvariant.html | 164 +++++++++++++++++++++++++--------------------- 2 files changed, 170 insertions(+), 132 deletions(-) diff --git a/doc/rvariant.adoc b/doc/rvariant.adoc index 63b2c1c..a4cc7a8 100644 --- a/doc/rvariant.adoc +++ b/doc/rvariant.adoc @@ -25,6 +25,7 @@ Yaito Kakeyama; Nana Sakisaka :unwrap_recursive_type: <> :unwrap_recursive: pass:quotes[xref:#rvariant.recursive.helper[unwrap_recursive]] :recursive_wrapper: pass:macros[xref:#rvariant.recursive[recursive_wrapper]] +:recursive_wrapper_alloca: pass:macros[xref:#rvariant.recursive[recursive_wrapper_alloca]] :subset_of: <> :equivalent_to: <> @@ -301,8 +302,10 @@ template class rvariant; // <>, class template pass:quotes[`recursive_wrapper`] -template> +template class recursive_wrapper; +template> +class recursive_wrapper_alloca; /* all features commented below defined as per https://eel.is/c+\+draft/variant[[variant\]] */ // variant_size, variant_size_v @@ -376,8 +379,10 @@ template /* constexpr */ std::size_t hash_value(rvariant const&); // <>, hash support +template + /* constexpr */ std::size_t hash_value(recursive_wrapper const&); template - /* constexpr */ std::size_t hash_value(recursive_wrapper const&); + /* constexpr */ std::size_t hash_value(recursive_wrapper_alloca const&); // <>, pass:quotes[`recursive_wrapper`] helper classes template using unwrap_recursive_type = {see-below}; @@ -499,9 +504,9 @@ NOTE: See also: spec of https://eel.is/c++draft/variant[`std::variant`] and http * All types in `Ts` must satisfy all requirements on the corresponding parameter in `std::variant`, unless otherwise noted. -* [.underline]#Let `T` and `A` denote arbitrary types. For the template parameter of `rvariant`, if a user provides both `T` and `{recursive_wrapper}` , the program is ill-formed.# +* [.underline]#Let `T` denote an arbitrary type. For the template parameter of `rvariant`, if a user provides both `T` and any instantiation of `{recursive_wrapper}` or `{recursive_wrapper_alloca}` that has the `value_type` of `T`, the program is ill-formed.# -* [.underline]#Let `T` denote an arbitrary type. For the template parameter of `rvariant`, if a user provides multiple different specializations of `{recursive_wrapper}` such that the first template parameter is `T`, the program is ill-formed.# +* [.underline]#Let `T` denote an arbitrary type. For the template parameter of `rvariant`, if a user provides multiple different instantiations of `{recursive_wrapper_alloca}` such that the first template parameter is `T`, the program is ill-formed.# [WARNING] -- @@ -510,13 +515,14 @@ Although `rvariant` is normally capable of holding duplicate alternatives, the a [,cpp,subs="+macros,+attributes"] ---- rvariant< - int, recursive_wrapper, recursive_wrapper> + int, recursive_wrapper, recursive_wrapper_alloca> > v(42); // error-prone; not allowed ---- -- -* Let `VT~_i_~` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT~_i_~` denote `T~_i_~`. Let `U~_j_~` denote the _j_^th^ type of the template parameter pack having the name `Us` on each flexibility-related functions. [.underline]#The _corresponding alternative_ for `rvariant` is the first type for which `std::is_same_v<{unwrap_recursive_type}, {unwrap_recursive_type}>` is `true`#. +* If `{recursive_wrapper}` or `{recursive_wrapper_alloca}` (with any type `A`) occurs anywhere in `Ts\...`, let `VT~_i_~` denote that type; otherwise, let `VT~_i_~` denote `T~_i_~`. Let `U~_j_~` denote the _j_^th^ type of the template parameter pack having the name `Us` on each flexibility-related functions. [.underline]#The _corresponding alternative_ for `rvariant` is the first type for which `std::is_same_v<{unwrap_recursive_type}, {unwrap_recursive_type}>` is `true`#. +* For the function that has the formal template parameter named `T`: if `{recursive_wrapper}` or `{recursive_wrapper_alloca}` (with any type `A`) occurs anywhere in `Ts\...`, let `VT` denote that type; otherwise, let `VT` denote `T`. [[rvariant.ctor]] === Constructors [.slug]##<>## @@ -563,9 +569,7 @@ include::_std-variant-proxy.adoc[] + *_Postconditions:_* `holds_alternative[.underline]##<{unwrap_recursive_type}>##(*this)` is `true`. -* [.candidate]#5)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}`#. -+ -[.underline]#Let `VT` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT` denote `T`#. +* [.candidate]#5)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`#. + *_Constraints:_* + @@ -583,9 +587,7 @@ include::_std-variant-proxy.adoc[] + *_Remarks:_* If [.underline]#`VT`#'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor. -* [.candidate]#6)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}`#. -+ -[.underline]#Let `VT` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT` denote `T`.# +* [.candidate]#6)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`#. + *_Constraints:_* + @@ -785,9 +787,7 @@ constexpr variant_alternative_t>& [.candidates] :spec-url: https://eel.is/c++draft/variant.mod -* [.candidate]#1)# [.underline]#Let `VT` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT` denote `T`.# -+ -*_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}`.# +* [.candidate]#1)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# + *_Constraints:_* `std::is_constructible_v<[.underline]##VT##, Args\...>` is `true`, and `T` occurs exactly once in [.underline]#`{unwrap_recursive_type}`#. + @@ -795,9 +795,7 @@ constexpr variant_alternative_t>& pass:quotes[  ]`return emplace<__I__>(std::forward(args)\...);` + where `_I_` is the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. -* [.candidate]#2)# [.underline]#Let `VT` denote `{recursive_wrapper}` (for any type `A`) if such a specialization occurs anywhere in `Ts\...`; otherwise, let `VT` denote `T`.# -+ -*_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}`.# +* [.candidate]#2)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# + *_Constraints:_* `std::is_constructible_v<[.underline]##VT##, std::initializer_list&, Args\...>` is `true`, and `T` occurs exactly once in [.underline]#`{unwrap_recursive_type}`#. + @@ -809,13 +807,13 @@ where `_I_` is the zero-based index of `T` in [.underline]#`{unwrap_recursive_ty + *_Returns:_* [.underline]#Let `o` denote# a reference to the new contained value. [.underline]#Returns `{unwrap_recursive}(o)`.# + -*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `std::forward(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)# +*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `std::forward(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)# * [.candidate]#4)# Equivalent to the `std::variant` counterpart, ^https://eel.is/c++draft/variant.mod[[spec\]]^ except: + *_Returns:_* [.underline]#Let `o` denote# a reference to the new contained value. [.underline]#Returns `{unwrap_recursive}(o)`.# + -*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `il, std::forward(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)# +*_Remarks:_* [.underline]#If `T~_I_~` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, this function is permitted to construct an intermediate variable `tmp` as if by passing `il, std::forward(args)\...` to ``T~_I_~``'s constructor. Then `rvariant` direct-non-list-initializes the contained value of `T~_I_~` with the argument `std::move(tmp)`. (_Note:_ This allows optimization where `rvariant` can be assumed to become never valueless on certain cases.)# [[rvariant.status]] === Value status [.slug]##<>## @@ -987,7 +985,7 @@ constexpr bool holds_alternative(rvariant const& v) noexcept; + *_Returns:_* `true` if `v.index()` is equal to the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. + -*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}`.# +*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# [,cpp,subs="+macros,+attributes"] @@ -1010,7 +1008,7 @@ constexpr {see-below} const&& pass:quotes[_GET_](rvariant const&& v); // + *_Preconditions:_* `v.index()` is `I`. + -*_Returns:_* [.underline]#`o`, where `o` denotes a reference to the object stored in `v`, if the type of the expression's receiver is a specialization of `{recursive_wrapper}`; otherwise, returns `{unwrap_recursive}(o)`#. +*_Returns:_* [.underline]#`o`, where `o` denotes a reference to the object stored in `v`, if the type of the expression's receiver is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`; otherwise, returns `{unwrap_recursive}(o)`#. [,cpp,subs="+macros,+attributes"] @@ -1059,7 +1057,7 @@ template constexpr T const&& get(rvariant const&& v + *_Effects:_* [.underline]#Let `VT` denote the type of the alternative held by `v`. If `{unwrap_recursive_type}` is the same type as `T`#, returns [.underline]#`{unwrap_recursive}(o)`, where `o` denotes a reference to the object stored in the `rvariant`#. Otherwise, throws an exception of type {bad-variant-access}. + -*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}`.# +*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# [,cpp,subs="+macros,+attributes"] @@ -1103,7 +1101,7 @@ constexpr std::add_pointer_t + *_Effects:_* Equivalent to: `return get_if<__i__>(v);` with _i_ being the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. + -*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}`.# +*_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# [[rvariant.visit]] @@ -1149,38 +1147,49 @@ namespace std { template struct hash<::temp_ns::rvariant>;pass:quotes[[.candidate\]#// 1#] +template +struct hash<::temp_ns::recursive_wrapper>;pass:quotes[[.candidate\]#// 2#] + template -struct hash<::temp_ns::recursive_wrapper>;pass:quotes[[.candidate\]#// 2#] +struct hash<::temp_ns::recursive_wrapper_alloca>;pass:quotes[[.candidate\]#// 3#] } // std ---- +[.candidates] +-- +:spec-url: https://eel.is/c++draft/variant.hash +* [.candidate]#1)# +include::_std-variant-proxy.adoc[] + +:spec-url: https://eel.is/c++draft/indirect.hash +* [.candidate]#2-3)# +include::_std-indirect-proxy.adoc[] +-- + [,cpp,subs="+macros,+attributes"] ---- namespace temp_ns { template -/* constexpr */ std::size_t hash_value(rvariant const& v);pass:quotes[[.candidate\]#// 3#] +/* constexpr */ std::size_t hash_value(rvariant const& v);pass:quotes[[.candidate\]#// 1#] + +template +/* constexpr */ std::size_t hash_value(recursive_wrapper const& rw);pass:quotes[[.candidate\]#// 2#] template -/* constexpr */ std::size_t hash_value(recursive_wrapper const& rw);pass:quotes[[.candidate\]#// 4#] +/* constexpr */ std::size_t hash_value(recursive_wrapper_alloca const& rw);pass:quotes[[.candidate\]#// 3#] } // temp_ns ---- [.candidates] -- -:spec-url: https://eel.is/c++draft/variant.hash -* [.candidate]#1)# -include::_std-variant-proxy.adoc[] - -:spec-url: https://eel.is/c++draft/indirect.hash -* [.candidate]#2)# -include::_std-indirect-proxy.adoc[] +* [.candidate]#1)# *_Effects:_* Equivalent to `std::hash>{}(v)`. -* [.candidate]#3)# *_Effects:_* Equivalent to `std::hash>{}(v)`. +* [.candidate]#2)# *_Effects:_* Equivalent to `std::hash>{}(rw)`. -* [.candidate]#4)# *_Effects:_* Equivalent to `std::hash>{}(rw)`. +* [.candidate]#3)# *_Effects:_* Equivalent to `std::hash>{}(rw)`. -- @@ -1357,31 +1366,44 @@ format_by(VFormat&& v_fmt, Variant&& v) noexcept;pass:quotes[[.candidate\]#// 1# [,cpp,subs="+macros,+attributes"] ---- -#include -#include +namespace temp_ns { + +template +class recursive_wrapper +{ + {see-below} +}; + +} // temp_ns +---- + +Class template `recursive_wrapper` behaves like `recursive_wrapper_alloca` instantiated with `std::allocator`, except that the allocator-related member functions (that is, member functions that take allocator-specific arguments) are omitted. +(_Note:_ this is a QoL feature to discard the rarely used `std::allocator` type parameter from diagnostic messages; see https://github.com/iris-cpp/iris/issues/43[iris-cpp/iris#43].) +[,cpp,subs="+macros,+attributes"] +---- namespace temp_ns { template> -class recursive_wrapper +class recursive_wrapper_alloca { // provides the same functionality as https://eel.is/c+\+draft/indirect[pass:quotes[`std::indirect`]], unless otherwise noted // <>, constructors - constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(); + constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper_alloca(); template - constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(U&& x); + constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper_alloca(U&& x); }; // equivalent to the https://eel.is/c+\+draft/indirect[pass:quotes[`std::indirect`]] counterpart template - recursive_wrapper(Value) -> recursive_wrapper; + recursive_wrapper_alloca(Value) -> recursive_wrapper_alloca; // equivalent to the https://eel.is/c++draft/indirect[pass:quotes[`std::indirect`]] counterpart template - recursive_wrapper(std::allocator_arg_t, Allocator, Value) - -> recursive_wrapper< + recursive_wrapper_alloca(std::allocator_arg_t, Allocator, Value) + -> recursive_wrapper_alloca< Value, typename std::allocator_traits::template rebind_alloc >; @@ -1398,7 +1420,9 @@ template namespace temp_ns::pmr { template -using recursive_wrapper = ::temp_ns::recursive_wrapper>; +using recursive_wrapper = ::temp_ns::recursive_wrapper_alloca< + T, std::pmr::polymorphic_allocator +>; } // temp_ns::pmr ---- @@ -1406,15 +1430,11 @@ using recursive_wrapper = ::temp_ns::recursive_wrapper>## -Unless otherwise noted, the class template `temp_ns::recursive_wrapper` and relevant components in the namespace scope provide same functionality and have equivalent requirements as `std::indirect`, except that: - -[none] -* -- The class name is `recursive_wrapper`. -* -- `std::indirect` and `temp_ns::recursive_wrapper` are distinguishable in type level. +Unless otherwise noted, the class template `recursive_wrapper_alloca` and relevant components in the namespace scope provide same functionality and have equivalent requirements as `std::indirect`. -WARNING: `temp_ns::recursive_wrapper` is *not* a type alias of `std::indirect` and does *not* publicly derive from it. +WARNING: `recursive_wrapper` and `recursive_wrapper_alloca` are *not* type alias of `std::indirect` and do *not* publicly derive from it. -NOTE: Although `std::indirect` is a {cpp}26 feature, `temp_ns::recursive_wrapper` can be used in {cpp}23. +NOTE: Although `std::indirect` is a {cpp}26 feature, `recursive_wrapper` and `recursive_wrapper_alloca` can be used in {cpp}23. [[rvariant.recursive.ctor]] @@ -1423,10 +1443,10 @@ Effectively overrides only the ones listed below; rest are the same as `std::ind [,cpp,subs="+macros,+attributes"] ---- -constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper();pass:quotes[[.candidate\]#// 1#] +constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper_alloca();pass:quotes[[.candidate\]#// 1#] template -constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(U&& u);pass:quotes[[.candidate\]#// 2#] +constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper_alloca(U&& u);pass:quotes[[.candidate\]#// 2#] ---- [.candidates] @@ -1436,7 +1456,7 @@ constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(U&& u + -- [none] -** -- `std::is_same_v, recursive_wrapper>` is `false`, and +** -- `std::is_same_v, recursive_wrapper_alloca>` is `false`, and ** -- `std::is_same_v, std::in_place_t>` is `false`, and ** -- `std::is_default_constructible_v` is `true`, and ** -- [.underline]#`std::is_convertible_v` is `true`#. @@ -1445,7 +1465,7 @@ constexpr pass:quotes[[.underline\]#/* not explicit */#] recursive_wrapper(U&& u _Note 1:_ [.underline]#This prevents recursive instantiation of `std::is_constructible`#, even for recursive types, while preserving SFINAE-friendliness. This specification is technically viable only because the class template `rvariant` never uses `std::is_convertible` in any of its constructor overloads. As a result, the atomic constraints of `rvariant` and `recursive_wrapper` remain mutually exclusive. However, if a user-defined class depends on _both_ `std::is_constructible` and `std::is_convertible` (for the same `rvariant` specialization), it may trigger recursive instantiation. + -_Note 2:_ It is currently unknown whether the recursive instantiation scenario described in _Note 1_ can be technically avoided. This note is provided for informational purposes only and does not specify the semantics of `recursive_wrapper`. +_Note 2:_ It is currently unknown whether the recursive instantiation scenario described in _Note 1_ can be technically avoided without depending on the fragile mutual-exclusiveness on `std::is_constructible` and `std::is_convertible`. If you are aware of any technical insights, please contact us at https://github.com/iris-cpp/iris/issues[iris-cpp/iris]. -- + *_Effects:_* Equivalent to the `std::indirect` counterpart. ^link:pass:[https://eel.is/c++draft/indirect.ctor#lib:indirect,constructor______][[spec\]]^ @@ -1465,7 +1485,7 @@ using unwrap_recursive_type = {see-below}; ---- [.candidates] -* [.candidate]#{empty}# Denotes `T::value_type` if `T` is a specialization of `{recursive_wrapper}`. Otherwise, denotes `T`. +* [.candidate]#{empty}# Denotes `T::value_type` if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`. Otherwise, denotes `T`. [,cpp,subs="+macros,+attributes"] ---- @@ -1478,7 +1498,7 @@ constexpr auto&& unwrap_recursive(T&& o) noexcept; ---- [.candidates] -* [.candidate]#{empty}# *_Returns:_* `*o`, if cv-unqualified non-reference type for `T` is a specialization of `{recursive_wrapper}`. Otherwise, returns `o`. +* [.candidate]#{empty}# *_Returns:_* `*o`, if cv-unqualified non-reference type for `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`. Otherwise, returns `o`. + *_Remarks:_* `unwrap_recursive` is an _algorithm function object_ (link:https://eel.is/c++draft/alg.func.obj[[alg.func.obj\],window=_blank]). @@ -1506,7 +1526,7 @@ struct compact_alternative; A variant with a single alternative may introduce unnecessary overhead when used in many places where only the underlying type is actually needed. In such cases, the variant can be _unwrapped_ using `compact_alternative`. This is useful for resolving issues such as https://github.com/boostorg/spirit/issues/610[boostorg/spirit#610]. [WARNING] -`compact_alternative` does not unwrap `{recursive_wrapper}`. This is intentional, because doing so could lead to instantiating incomplete type on undesired timings. You may apply `{unwrap_recursive_type}` manually. +`compact_alternative` does not unwrap `{recursive_wrapper}` or `{recursive_wrapper_alloca}`. This is intentional, because doing so could lead to instantiating incomplete type on undesired timings. You may apply `{unwrap_recursive_type}` manually. [[rvariant.xo]] diff --git a/doc/rvariant.html b/doc/rvariant.html index 71e11eb..4359a11 100644 --- a/doc/rvariant.html +++ b/doc/rvariant.html @@ -1264,8 +1264,10 @@

Header <temp class rvariant; // [rvariant.recursive], class template recursive_wrapper -template<class T, class Allocator = std::allocator<T>> +template<class T> class recursive_wrapper; +template<class T, class Allocator = std::allocator<T>> +class recursive_wrapper_alloca; /* all features commented below defined as per [variant] */ // variant_size, variant_size_v @@ -1339,8 +1341,10 @@

Header <temp /* constexpr */ std::size_t hash_value(rvariant<Ts...> const&); // [rvariant.hash], hash support +template<class T> + /* constexpr */ std::size_t hash_value(recursive_wrapper<T> const&); template<class T, class Allocator> - /* constexpr */ std::size_t hash_value(recursive_wrapper<T, Allocator> const&); + /* constexpr */ std::size_t hash_value(recursive_wrapper_alloca<T, Allocator> const&); // [rvariant.recursive.helper], recursive_wrapper helper classes template<class T> using unwrap_recursive_type = see below; @@ -1476,10 +1480,10 @@

Let T and A denote arbitrary types. For the template parameter of rvariant, if a user provides both T and recursive_wrapper<T, A> , the program is ill-formed.

+

Let T denote an arbitrary type. For the template parameter of rvariant, if a user provides both T and any instantiation of recursive_wrapper or recursive_wrapper_alloca that has the value_type of T, the program is ill-formed.

  • -

    Let T denote an arbitrary type. For the template parameter of rvariant, if a user provides multiple different specializations of recursive_wrapper such that the first template parameter is T, the program is ill-formed.

    +

    Let T denote an arbitrary type. For the template parameter of rvariant, if a user provides multiple different instantiations of recursive_wrapper_alloca such that the first template parameter is T, the program is ill-formed.

  • @@ -1496,7 +1500,7 @@

    rvariant<
    -  int, recursive_wrapper<int>, recursive_wrapper<int, MyAllocator<int>>
    +  int, recursive_wrapper<int>, recursive_wrapper_alloca<int, MyAllocator<int>>
     > v(42); // error-prone; not allowed
    @@ -1507,7 +1511,10 @@

    • -

      Let VTi denote recursive_wrapper<Ti, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VTi denote Ti. Let Uj denote the jth type of the template parameter pack having the name Us on each flexibility-related functions. The corresponding alternative for rvariant is the first type for which std::is_same_v<unwrap_recursive_type<VTi>, unwrap_recursive_type<Uj>> is true.

      +

      If recursive_wrapper<Ti> or recursive_wrapper_alloca<Ti, A> (with any type A) occurs anywhere in Ts..., let VTi denote that type; otherwise, let VTi denote Ti. Let Uj denote the jth type of the template parameter pack having the name Us on each flexibility-related functions. The corresponding alternative for rvariant is the first type for which std::is_same_v<unwrap_recursive_type<VTi>, unwrap_recursive_type<Uj>> is true.

      +
    • +
    • +

      For the function that has the formal template parameter named T: if recursive_wrapper<T> or recursive_wrapper_alloca<T, A> (with any type A) occurs anywhere in Ts..., let VT denote that type; otherwise, let VT denote T.

    @@ -1560,10 +1567,7 @@

    Constructors
  • -

    5) Mandates: T is not a specialization of recursive_wrapper.

    -
    -

    Let VT denote recursive_wrapper<T, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VT denote T.

    -
    +

    5) Mandates: T is not a specialization of recursive_wrapper or recursive_wrapper_alloca.

    Constraints:

    @@ -1595,10 +1599,7 @@

    Constructors

  • -

    6) Mandates: T is not a specialization of recursive_wrapper.

    -
    -

    Let VT denote recursive_wrapper<T, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VT denote T.

    -
    +

    6) Mandates: T is not a specialization of recursive_wrapper or recursive_wrapper_alloca.

    Constraints:

    @@ -1931,10 +1932,7 @@

    Modifiers
    • -

      1) Let VT denote recursive_wrapper<T, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VT denote T.

      -
      -

      Mandates: T is not a specialization of recursive_wrapper.

      -
      +

      1) Mandates: T is not a specialization of recursive_wrapper or recursive_wrapper_alloca.

      Constraints: std::is_constructible_v<VT, Args...> is true, and T occurs exactly once in unwrap_recursive_type<Ts>.

      @@ -1945,10 +1943,7 @@

      Modifiers

    • -

      2) Let VT denote recursive_wrapper<T, A> (for any type A) if such a specialization occurs anywhere in Ts...; otherwise, let VT denote T.

      -
      -

      Mandates: T is not a specialization of recursive_wrapper.

      -
      +

      2) Mandates: T is not a specialization of recursive_wrapper or recursive_wrapper_alloca.

      Constraints: std::is_constructible_v<VT, std::initializer_list<U>&, Args...> is true, and T occurs exactly once in unwrap_recursive_type<Ts>.

      @@ -1964,7 +1959,7 @@

      Modifiers Returns: Let o denote a reference to the new contained value. Returns unwrap_recursive(o).

      -

      Remarks: If TI is a specialization of recursive_wrapper, this function is permitted to construct an intermediate variable tmp as if by passing std::forward<Args>(args)... to TI's constructor. Then rvariant direct-non-list-initializes the contained value of TI with the argument std::move(tmp). (Note: This allows optimization where rvariant can be assumed to become never valueless on certain cases.)

      +

      Remarks: If TI is a specialization of recursive_wrapper or recursive_wrapper_alloca, this function is permitted to construct an intermediate variable tmp as if by passing std::forward<Args>(args)... to TI's constructor. Then rvariant direct-non-list-initializes the contained value of TI with the argument std::move(tmp). (Note: This allows optimization where rvariant can be assumed to become never valueless on certain cases.)

    • @@ -1973,7 +1968,7 @@

      Modifiers Returns: Let o denote a reference to the new contained value. Returns unwrap_recursive(o).

      -

      Remarks: If TI is a specialization of recursive_wrapper, this function is permitted to construct an intermediate variable tmp as if by passing il, std::forward<Args>(args)... to TI's constructor. Then rvariant direct-non-list-initializes the contained value of TI with the argument std::move(tmp). (Note: This allows optimization where rvariant can be assumed to become never valueless on certain cases.)

      +

      Remarks: If TI is a specialization of recursive_wrapper or recursive_wrapper_alloca, this function is permitted to construct an intermediate variable tmp as if by passing il, std::forward<Args>(args)... to TI's constructor. Then rvariant direct-non-list-initializes the contained value of TI with the argument std::move(tmp). (Note: This allows optimization where rvariant can be assumed to become never valueless on certain cases.)

    @@ -2198,7 +2193,7 @@

    Value access Returns: true if v.index() is equal to the zero-based index of T in unwrap_recursive_type<Ts>.

    -

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper.

    +

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper or recursive_wrapper_alloca.

  • @@ -2226,7 +2221,7 @@

    Value access Preconditions: v.index() is I.

    -

    Returns: o, where o denotes a reference to the object stored in v, if the type of the expression’s receiver is a specialization of recursive_wrapper; otherwise, returns unwrap_recursive(o).

    +

    Returns: o, where o denotes a reference to the object stored in v, if the type of the expression’s receiver is a specialization of recursive_wrapper or recursive_wrapper_alloca; otherwise, returns unwrap_recursive(o).

    @@ -2284,7 +2279,7 @@

    Value access Effects: Let VT denote the type of the alternative held by v. If unwrap_recursive_type<VT> is the same type as T, returns unwrap_recursive(o), where o denotes a reference to the object stored in the rvariant. Otherwise, throws an exception of type std::bad_variant_access.

    -

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper.

    +

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper or recursive_wrapper_alloca.

    @@ -2337,7 +2332,7 @@

    Value access Effects: Equivalent to: return get_if<i>(v); with i being the zero-based index of T in unwrap_recursive_type<Ts>.

    -

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper.

    +

    Remarks: This function is defined as deleted if T is a specialization of recursive_wrapper or recursive_wrapper_alloca.

    @@ -2397,21 +2392,47 @@

    Hash support template<class... Ts> struct hash<::temp_ns::rvariant<Ts...>>;// 1 +template<class T> +struct hash<::temp_ns::recursive_wrapper<T>>;// 2 + template<class T, class Allocator> -struct hash<::temp_ns::recursive_wrapper<T, Allocator>>;// 2 +struct hash<::temp_ns::recursive_wrapper_alloca<T, Allocator>>;// 3 } // std +
    +
    +
    +
      +
    • +

      1) +Equivalent to the std::variant counterpart. [spec]

      +
    • +
    +
    +
    +
      +
    • +

      2-3) +Equivalent to the std::indirect counterpart. [spec]

      +
    • +
    +
    +
    +
    namespace temp_ns {
     
     template<class... Ts>
    -/* constexpr */ std::size_t hash_value(rvariant<Ts...> const& v);// 3
    +/* constexpr */ std::size_t hash_value(rvariant<Ts...> const& v);// 1
    +
    +template<class T>
    +/* constexpr */ std::size_t hash_value(recursive_wrapper<T> const& rw);// 2
     
     template<class T, class Allocator>
    -/* constexpr */ std::size_t hash_value(recursive_wrapper<T, Allocator> const& rw);// 4
    +/* constexpr */ std::size_t hash_value(recursive_wrapper_alloca<T, Allocator> const& rw);// 3
     
     } // temp_ns
    @@ -2421,22 +2442,13 @@

    Hash support
    • -

      1) -Equivalent to the std::variant counterpart. [spec]

      -
    • -
    -
    -
    -
      -
    • -

      2) -Equivalent to the std::indirect counterpart. [spec]

      +

      1) Effects: Equivalent to std::hash<rvariant<Ts...>>{}(v).

    • -

      3) Effects: Equivalent to std::hash<rvariant<Ts...>>{}(v).

      +

      2) Effects: Equivalent to std::hash<recursive_wrapper<T>>{}(rw).

    • -

      4) Effects: Equivalent to std::hash<recursive_wrapper<T, Allocator>>{}(rw).

      +

      3) Effects: Equivalent to std::hash<recursive_wrapper_alloca<T, Allocator>>{}(rw).

    @@ -2691,31 +2703,45 @@

    Cla
    -
    #include <compare>
    -#include <memory>
    +
    namespace temp_ns {
     
    -namespace temp_ns {
    +template<class T>
    +class recursive_wrapper
    +{
    +  see below
    +};
    +
    +} // temp_ns
    +
    +
    +
    +

    Class template recursive_wrapper behaves like recursive_wrapper_alloca instantiated with std::allocator<T>, except that the allocator-related member functions (that is, member functions that take allocator-specific arguments) are omitted. +(Note: this is a QoL feature to discard the rarely used std::allocator<T> type parameter from diagnostic messages; see iris-cpp/iris#43.)

    +
    +
    +
    +
    namespace temp_ns {
     
     template<class T, class Allocator = std::allocator<T>>
    -class recursive_wrapper
    +class recursive_wrapper_alloca
     {
       // provides the same functionality as std::indirect, unless otherwise noted
     
       // [rvariant.recursive.ctor], constructors
    -  constexpr /* not explicit */ recursive_wrapper();
    +  constexpr /* not explicit */ recursive_wrapper_alloca();
     
       template<class U = T>
    -  constexpr /* not explicit */ recursive_wrapper(U&& x);
    +  constexpr /* not explicit */ recursive_wrapper_alloca(U&& x);
     };
     
     // equivalent to the std::indirect counterpart
     template<class Value>
    -  recursive_wrapper(Value) -> recursive_wrapper<Value>;
    +  recursive_wrapper_alloca(Value) -> recursive_wrapper_alloca<Value>;
     
     // equivalent to the std::indirect counterpart
     template<class Allocator, class Value>
    -  recursive_wrapper(std::allocator_arg_t, Allocator, Value)
    -    -> recursive_wrapper<
    +  recursive_wrapper_alloca(std::allocator_arg_t, Allocator, Value)
    +    -> recursive_wrapper_alloca<
           Value,
           typename std::allocator_traits<Allocator>::template rebind_alloc<Value>
         >;
    @@ -2732,7 +2758,9 @@ 

    Cla namespace temp_ns::pmr { template<class T> -using recursive_wrapper = ::temp_ns::recursive_wrapper<T, std::pmr::polymorphic_allocator<T>>; +using recursive_wrapper = ::temp_ns::recursive_wrapper_alloca< + T, std::pmr::polymorphic_allocator<T> +>; } // temp_ns::pmr

    @@ -2740,17 +2768,7 @@

    Cla

    General [rvariant.recursive.general]

    -

    Unless otherwise noted, the class template temp_ns::recursive_wrapper and relevant components in the namespace scope provide same functionality and have equivalent requirements as std::indirect, except that:

    -
    -
    -
      -
    • -

       — The class name is recursive_wrapper.

      -
    • -
    • -

       — std::indirect and temp_ns::recursive_wrapper are distinguishable in type level.

      -
    • -
    +

    Unless otherwise noted, the class template recursive_wrapper_alloca and relevant components in the namespace scope provide same functionality and have equivalent requirements as std::indirect.

    @@ -2848,7 +2866,7 @@

    @@ -2865,7 +2883,7 @@

    • -

      Returns: *o, if cv-unqualified non-reference type for T is a specialization of recursive_wrapper. Otherwise, returns o.

      +

      Returns: *o, if cv-unqualified non-reference type for T is a specialization of recursive_wrapper or recursive_wrapper_alloca. Otherwise, returns o.

      Remarks: unwrap_recursive is an algorithm function object ([alg.func.obj]).

      @@ -2917,7 +2935,7 @@

      Pack manipula -compact_alternative does not unwrap recursive_wrapper. This is intentional, because doing so could lead to instantiating incomplete type on undesired timings. You may apply unwrap_recursive_type manually. +compact_alternative does not unwrap recursive_wrapper or recursive_wrapper_alloca. This is intentional, because doing so could lead to instantiating incomplete type on undesired timings. You may apply unwrap_recursive_type manually. From 98b192870f4e129ab1b4410de34545a6178590d6 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:33:11 +0900 Subject: [PATCH 07/10] Remove obsolete contents --- doc/rvariant.adoc | 82 --------------------------- doc/rvariant.html | 140 ---------------------------------------------- 2 files changed, 222 deletions(-) diff --git a/doc/rvariant.adoc b/doc/rvariant.adoc index a4cc7a8..a13e8a1 100644 --- a/doc/rvariant.adoc +++ b/doc/rvariant.adoc @@ -388,12 +388,6 @@ template template using unwrap_recursive_type = {see-below}; template constexpr auto&& unwrap_recursive(T&& o) noexcept; -// <>, pack manipulation and deduping -template class TT, class A, class B> - struct compact_alternative; -template class TT, class A, class B> - using compact_alternative_t = typename compact_alternative::type; - } // temp_ns ---- @@ -1503,75 +1497,6 @@ constexpr auto&& unwrap_recursive(T&& o) noexcept; *_Remarks:_* `unwrap_recursive` is an _algorithm function object_ (link:https://eel.is/c++draft/alg.func.obj[[alg.func.obj\],window=_blank]). -[[rvariant.pack]] -== Pack manipulation and deduping [.slug]##<>## - -[,cpp,subs="+macros,+attributes"] ----- -namespace temp_ns { - -template class TT, class A, class B> -struct compact_alternative; - -} // temp_ns ----- - -[.candidates] -* [.candidate]#{empty}# Effectively concatenates contained types in `A` and `B`, then dedupes them. If the resulting type list consists of only a single type, the surrounding template is unwrapped. -+ -*_Definition:_* Let `Ts` denote an imaginary pack of types where `TT` is the same type as `<>`. The member typedef `type` denotes `Ts\...[0]` if `sizeof\...(Ts) == 1`; otherwise, the member typedef `type` denotes `TT`. - -[WARNING] -.Notes on single-type variant -A variant with a single alternative may introduce unnecessary overhead when used in many places where only the underlying type is actually needed. In such cases, the variant can be _unwrapped_ using `compact_alternative`. This is useful for resolving issues such as https://github.com/boostorg/spirit/issues/610[boostorg/spirit#610]. - -[WARNING] -`compact_alternative` does not unwrap `{recursive_wrapper}` or `{recursive_wrapper_alloca}`. This is intentional, because doing so could lead to instantiating incomplete type on undesired timings. You may apply `{unwrap_recursive_type}` manually. - - -[[rvariant.xo]] -== Exposition-only utilities [.slug]##<>## -This section demonstrates internal features used in the implementation. - - -[[rvariant.xo.pack]] -=== Pack utilities [.slug]##<>## - -[,cpp,subs="+macros,+attributes"] ----- -template class TT, class A, class B> -using pass:quotes[_pack-union-t_] = {see-below}; // {exposition-only} ----- - -[.candidates] -* [.candidate]#{empty}# Let `As` denote the pack of template parameters of `A` if `A` is a specialization of `TT`, otherwise let `As` denote a pack of single type `A`. Let `Bs` denote likewise. `_pack-union-t_` denotes `TT`, where `Ts\...` is the set union of `As\...` and `Bs\...` expanded from left to right. For duplicate types, the first occurrence shall remain in `Ts\...`. - - -[[rvariant.xo.core]] -=== Core type traits [.slug]##<>## - -[,cpp,subs="+macros,+attributes"] ----- -template class TT> -struct pass:quotes[_is-ttp-specialization-of_]; // {exposition-only}pass:quotes[[.candidate\]#// 1#] - -template class TT> -struct pass:quotes[_is-nttp-specialization-of_]; // {exposition-only}pass:quotes[[.candidate\]#// 2#] - -template -struct pass:quotes[_is-specialization-of_]; // {exposition-only}pass:quotes[[.candidate\]#// 3#] ----- - -[.candidates] --- -* [.candidate]#1-2)# Inherits `std::true_type` if and only if `T` is a specialization of `TT`; otherwise, inherits `std::false_type`. - -* [.candidate]#3)# If `_any-ttp_` is a template template parameter that consists of NTTP, equivalent to `_is-nttp-specialization-of_`; otherwise, equivalent to `_is-ttp-specialization-of_`. --- - -NOTE: `_is-specialization-of_` requires {cpp}26 reflection for a straightforward resolution. For older versions, it can be worked around by a compound `requires` expression. - - = Additional Information [[visitation-technique]] @@ -1595,13 +1520,6 @@ Early `std::variant` implementations used this _function-pointer-based dispatch_ Unfortunately, GCC enables the optimization only in limited scenarios (link:https://github.com/gcc-mirror/gcc/blob/679e24f5a751663998ff7202149a749e0f7251f9/libstdc%2B%2B-v3/include/std/variant#L1863[link,window=_blank]), and LLVM has reverted it due to unresolved issues (link:https://github.com/llvm/llvm-project/issues/62648#issuecomment-1832315651[link,window=_blank]). Our benchmark results reflect this status quo, with `rvariant` performing up to about 2x faster than GCC/Clang. -[[about]] -== About the Authors - -Yaito Kakeyama is a {cpp} enthusiast with a strong interest in language design and modern library development. He has contributed to several public efforts in the {cpp} community, including co-authoring LWG 4166 with Nana Sakisaka and submitting occasional compiler bug reports. He is the co-author of `rvariant` and has been deeply involved in its implementation. - -Nana Sakisaka has taken on an active maintainer role in Boost.Spirit since May 2025. The development of `rvariant` began as part of a broader effort to modernize the Boost.Spirit.X3 codebase. He is the co-author of `rvariant` and has focused on its rationale and specification wording. - [[license]] == License This library is distributed under the https://github.com/iris-cpp/iris/blob/main/LICENSE[MIT License]. diff --git a/doc/rvariant.html b/doc/rvariant.html index 4359a11..a70e789 100644 --- a/doc/rvariant.html +++ b/doc/rvariant.html @@ -905,19 +905,11 @@

      rvariantrecursive_wrapper helper utilities [rvariant.recursive.helper]

    • -
    • Pack manipulation and deduping [rvariant.pack]
    • -
    • Exposition-only utilities [rvariant.xo] - -
  • Additional Information
  • @@ -1350,12 +1342,6 @@

    Header <temp template<class T> using unwrap_recursive_type = see below; template<class T> constexpr auto&& unwrap_recursive(T&& o) noexcept; -// [rvariant.pack], pack manipulation and deduping -template<template<class...> class TT, class A, class B> - struct compact_alternative; -template<template<class...> class TT, class A, class B> - using compact_alternative_t = typename compact_alternative<TT, A, B>::type; - } // temp_ns

    @@ -2892,121 +2878,6 @@

    -

    Pack manipulation and deduping [rvariant.pack]

    -
    -
    -
    -
    namespace temp_ns {
    -
    -template<template<class...> class TT, class A, class B>
    -struct compact_alternative;
    -
    -} // temp_ns
    -
    -
    -
    -
      -
    • -

      Effectively concatenates contained types in A and B, then dedupes them. If the resulting type list consists of only a single type, the surrounding template is unwrapped.

      -
      -

      Definition: Let Ts denote an imaginary pack of types where TT<Ts...> is the same type as pack-union-t<TT, A, B>. The member typedef type denotes Ts...[0] if sizeof...(Ts) == 1; otherwise, the member typedef type denotes TT<Ts...>.

      -
      -
    • -
    -
    -
    - - - - - -
    - - -
    Notes on single-type variant
    -A variant with a single alternative may introduce unnecessary overhead when used in many places where only the underlying type is actually needed. In such cases, the variant can be unwrapped using compact_alternative. This is useful for resolving issues such as boostorg/spirit#610. -
    -
    -
    - - - - - -
    - - -compact_alternative does not unwrap recursive_wrapper or recursive_wrapper_alloca. This is intentional, because doing so could lead to instantiating incomplete type on undesired timings. You may apply unwrap_recursive_type manually. -
    -
    -
    -

    -
    -

    Exposition-only utilities [rvariant.xo]

    -
    -
    -

    This section demonstrates internal features used in the implementation.

    -
    -
    -

    Pack utilities [rvariant.xo.pack]

    -
    -
    -
    template<template<class...> class TT, class A, class B>
    -using pack-union-t = see below; // exposition only
    -
    -
    -
    -
      -
    • -

      Let As denote the pack of template parameters of A if A is a specialization of TT, otherwise let As denote a pack of single type A. Let Bs denote likewise. pack-union-t denotes TT<Ts...>, where Ts... is the set union of As... and Bs... expanded from left to right. For duplicate types, the first occurrence shall remain in Ts....

      -
    • -
    -
    -
    -
    -

    Core type traits [rvariant.xo.core]

    -
    -
    -
    template<class T, template<class...> class TT>
    -struct is-ttp-specialization-of;  // exposition only// 1
    -
    -template<class T, template<auto...> class TT>
    -struct is-nttp-specialization-of; // exposition only// 2
    -
    -template<class T, any-ttp>
    -struct is-specialization-of;      // exposition only// 3
    -
    -
    -
    -
    -
    -
      -
    • -

      1-2) Inherits std::true_type if and only if T is a specialization of TT; otherwise, inherits std::false_type.

      -
    • -
    • -

      3) If any-ttp is a template template parameter that consists of NTTP, equivalent to is-nttp-specialization-of; otherwise, equivalent to is-ttp-specialization-of.

      -
    • -
    -
    -
    -
    -
    - - - - - -
    - - -is-specialization-of requires C++26 reflection for a straightforward resolution. For older versions, it can be worked around by a compound requires expression. -
    -
    -
    -
    -

    Additional Information

    Visitation Technique in Depth

    @@ -3035,17 +2906,6 @@

    -

    About the Authors

    -
    -
    -

    Yaito Kakeyama is a C++ enthusiast with a strong interest in language design and modern library development. He has contributed to several public efforts in the C++ community, including co-authoring LWG 4166 with Nana Sakisaka and submitting occasional compiler bug reports. He is the co-author of rvariant and has been deeply involved in its implementation.

    -
    -
    -

    Nana Sakisaka has taken on an active maintainer role in Boost.Spirit since May 2025. The development of rvariant began as part of a broader effort to modernize the Boost.Spirit.X3 codebase. He is the co-author of rvariant and has focused on its rationale and specification wording.

    -
    -
    -
    -

    License

    From 94639952ac994fe605767ab2c56365ceffbf8ec9 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:34:01 +0900 Subject: [PATCH 08/10] Replace `temp_ns` with `iris` --- doc/rvariant.adoc | 160 ++++++++++++++++++++++----------------------- doc/rvariant.html | 162 +++++++++++++++++++++++----------------------- 2 files changed, 161 insertions(+), 161 deletions(-) diff --git a/doc/rvariant.adoc b/doc/rvariant.adoc index a13e8a1..7a94b47 100644 --- a/doc/rvariant.adoc +++ b/doc/rvariant.adoc @@ -36,12 +36,12 @@ Yaito Kakeyama; Nana Sakisaka ---- // A common pattern for representing recursive ASTs using recursive variants. struct BinaryExpr; -using Expr = temp_ns::<>>>; +using Expr = iris::<>>>; enum class Op; struct BinaryExpr { Expr lhs, rhs; Op op{}; }; Expr expr{BinaryExpr{Expr{42}, Expr{3.14}}}; -expr.visit(temp_ns::overloaded{ +expr.visit(iris::overloaded{ [](int const&) { /* ... */ }, [](double const&) { /* ... */ }, [](BinaryExpr const&) { /* ... */ }, @@ -180,9 +180,9 @@ using A = int; using B = double; struct C {}; -using AB = temp_ns::<>; -using BA = temp_ns::rvariant; -using ABC = temp_ns::rvariant; +using AB = iris::<>; +using BA = iris::rvariant; +using ABC = iris::rvariant; // <> AB ab{42}; @@ -197,30 +197,30 @@ ab<><0>(123); // <> ab = AB{123}; -A& a = temp_ns::<><0>(ab); -A& a = temp_ns::get(ab); -C& c = temp_ns::get(ab); // throws {bad-variant-access} +A& a = iris::<><0>(ab); +A& a = iris::get(ab); +C& c = iris::get(ab); // throws {bad-variant-access} -A* a = temp_ns::<><0>(&ab); -A* a = temp_ns::get_if(&ab); -C* c = temp_ns::get_if(&ab); // `nullptr` +A* a = iris::<><0>(&ab); +A* a = iris::get_if(&ab); +C* c = iris::get_if(&ab); // `nullptr` // compatibility with boost; same effect as `get_if` -A* a = temp_ns::<><0>(&ab); -A* a = temp_ns::get(&ab); -C* c = temp_ns::get(&ab); // `nullptr` +A* a = iris::<><0>(&ab); +A* a = iris::get(&ab); +C* c = iris::get(&ab); // `nullptr` https://eel.is/{cxx}draft/variant.relops#lib:operator==,variant[ab pass:quotes[==] ab]; https://eel.is/{cxx}draft/variant.relops#lib:operator<,variant[ab pass:quotes[<] ab]; https://eel.is/{cxx}draft/variant.relops#lib:operator<=>,variant[ab pass:quotes[<=>] ab]; // `requires (https://en.cppreference.com/w/cpp/utility/compare/three_way_comparable[std::three_way_comparable] && ...)` -auto visitor = temp_ns::overloaded { +auto visitor = iris::overloaded { [](A const& a) {}, [](B const& b) {}, }; ab<>(visitor); // member visit -temp_ns::<>(visitor, ab); // function visit +iris::<>(visitor, ab); // function visit ---- @@ -238,9 +238,9 @@ temp_ns::<>(visitor, ab); // function visit abc = AB{}; // subset assignment } -static_assert(temp_ns::<> == 2); -static_assert(std::same_as><0, AB>, A>); -static_assert(temp_ns::<>(ab)); +static_assert(iris::<> == 2); +static_assert(std::same_as><0, AB>, A>); +static_assert(iris::<>(ab)); static_assert(!ab<>()); static_assert(ab<>() != https://eel.is/c++draft/variant.syn[std::variant_npos]); @@ -260,7 +260,7 @@ std::size_t _ = <>(ab); // compatibility with boost // <> { - using V = ::temp_ns::rvariant; + using V = ::iris::rvariant; // <> std::cout << V{42} << '\n'; // prints pass:quotes[`42`] @@ -268,9 +268,9 @@ std::size_t _ = <>(ab); // compatibility with boost // <> std::println("{}", V{42}); // prints pass:quotes[`42`] - constexpr auto v_fmt = temp_ns::variant_format_for("{:04d}", "{:.1f}"); - std::println("foo{}bar", temp_ns::format_by(v_fmt, V(42)); // prints pass:quotes[`foo0042bar`] - std::println("foo{}bar", temp_ns::format_by(v_fmt, V(3.14)); // prints pass:quotes[`foo3.1bar`] + constexpr auto v_fmt = iris::variant_format_for("{:04d}", "{:.1f}"); + std::println("foo{}bar", iris::format_by(v_fmt, V(42)); // prints pass:quotes[`foo0042bar`] + std::println("foo{}bar", iris::format_by(v_fmt, V(3.14)); // prints pass:quotes[`foo3.1bar`] } ---- @@ -285,7 +285,7 @@ std::size_t _ = <>(ab); // compatibility with boost [[rvariant.syn]] -== Header synopsis [.slug]##<>## +== Header synopsis [.slug]##<>## [,cpp,subs="+macros,+attributes"] ---- @@ -295,7 +295,7 @@ std::size_t _ = <>(ab); // compatibility with boost #include // for pass:quotes[`std::in_place_type`], etc. #include // for compatibility with {bad-variant-access}, etc. -namespace temp_ns { +namespace iris { // <>, class template pass:quotes[`rvariant`] template @@ -388,7 +388,7 @@ template template using unwrap_recursive_type = {see-below}; template constexpr auto&& unwrap_recursive(T&& o) noexcept; -} // temp_ns +} // iris ---- [,cpp,subs="+macros,+attributes"] @@ -396,10 +396,10 @@ template constexpr auto&& unwrap_recursive(T&& o) noexcept; namespace std { // <>, hash support -template struct hash<::temp_ns::rvariant>; +template struct hash<::iris::rvariant>; // <>, hash support -template struct hash<::temp_ns::recursive_wrapper>; +template struct hash<::iris::recursive_wrapper>; } // std ---- @@ -410,7 +410,7 @@ template struct hash<::temp_ns::recursive_wrapper class rvariant @@ -485,7 +485,7 @@ public: constexpr R visit(this Self&&, Visitor&&); }; -} // temp_ns +} // iris ---- @@ -902,7 +902,7 @@ include::_std-variant-proxy.adoc[] [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template struct variant_alternative; // not defined @@ -913,7 +913,7 @@ struct variant_alternative;pass:quotes[[.candidate\]#// 1#] template struct variant_alternative>;pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -931,7 +931,7 @@ include::_std-variant-proxy.adoc[] [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns::rvariant_set { +namespace iris::rvariant_set { template struct is_subset_of : std::false_type {};pass:quotes[[.candidate\]#// 1#] @@ -948,7 +948,7 @@ concept subset_of = is_subset_of_v; template concept equivalent_to = subset_of && subset_of; -} // temp_ns::rvariant_set +} // iris::rvariant_set ---- [.candidates] @@ -966,12 +966,12 @@ concept equivalent_to = subset_of && subset_of; [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr bool holds_alternative(rvariant const& v) noexcept; -} // temp_ns +} // iris ---- [.candidates] @@ -1007,7 +1007,7 @@ constexpr {see-below} const&& pass:quotes[_GET_](rvariant const&& v); // [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr variant_alternative_t>& @@ -1025,7 +1025,7 @@ template constexpr variant_alternative_t> const&& get(rvariant const&& v); -} // temp_ns +} // iris ---- [.candidates] @@ -1036,14 +1036,14 @@ constexpr variant_alternative_t> const&& [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr T& get(rvariant& v); template constexpr T&& get(rvariant&& v); template constexpr T const& get(rvariant const& v); template constexpr T const&& get(rvariant const&& v); -} // temp_ns +} // iris ---- [.candidates] @@ -1056,7 +1056,7 @@ template constexpr T const&& get(rvariant const&& v [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr std::add_pointer_t>> @@ -1066,7 +1066,7 @@ template constexpr std::add_pointer_t> const> get_if(rvariant const* v) noexcept;pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -1077,7 +1077,7 @@ constexpr std::add_pointer_t> const> [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr std::add_pointer_t @@ -1087,7 +1087,7 @@ template constexpr std::add_pointer_t get_if(rvariant const* v) noexcept;pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -1103,7 +1103,7 @@ constexpr std::add_pointer_t [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr {see-below} visit(Visitor&& vis, Variants&&... vars);pass:quotes[[.candidate\]#// 1#] @@ -1111,7 +1111,7 @@ constexpr {see-below} visit(Visitor&& vis, Variants&&... vars);pass:quotes[[.can template constexpr R visit(Visitor&& vis, Variants&&... vars);pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris // below are member functions of the class template <>: @@ -1128,7 +1128,7 @@ constexpr R visit(this Self&& self, Visitor&& vis);pass:quotes[[.candidate\]#// [none] ** -- `_GET_<__m__>(std::forward(vars))` is replaced with `{unwrap_recursive}(_GET_<__m__>(std::forward(vars)))`. -* [.candidate]#3-4)# Equivalent to the `std::variant` counterpart ^https://eel.is/c++draft/variant.visit[[spec\]]^, except that it forwards to `temp_ns::visit` instead of `std::visit`. +* [.candidate]#3-4)# Equivalent to the `std::variant` counterpart ^https://eel.is/c++draft/variant.visit[[spec\]]^, except that it forwards to `iris::visit` instead of `std::visit`. [[rvariant.hash]] @@ -1139,13 +1139,13 @@ constexpr R visit(this Self&& self, Visitor&& vis);pass:quotes[[.candidate\]#// namespace std { template -struct hash<::temp_ns::rvariant>;pass:quotes[[.candidate\]#// 1#] +struct hash<::iris::rvariant>;pass:quotes[[.candidate\]#// 1#] template -struct hash<::temp_ns::recursive_wrapper>;pass:quotes[[.candidate\]#// 2#] +struct hash<::iris::recursive_wrapper>;pass:quotes[[.candidate\]#// 2#] template -struct hash<::temp_ns::recursive_wrapper_alloca>;pass:quotes[[.candidate\]#// 3#] +struct hash<::iris::recursive_wrapper_alloca>;pass:quotes[[.candidate\]#// 3#] } // std ---- @@ -1163,7 +1163,7 @@ include::_std-indirect-proxy.adoc[] [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template /* constexpr */ std::size_t hash_value(rvariant const& v);pass:quotes[[.candidate\]#// 1#] @@ -1174,7 +1174,7 @@ template template /* constexpr */ std::size_t hash_value(recursive_wrapper_alloca const& rw);pass:quotes[[.candidate\]#// 3#] -} // temp_ns +} // iris ---- [.candidates] @@ -1189,18 +1189,18 @@ template [[rvariant.io]] == I/O [.slug]##<>## -I/O components are _not_ included by the global convenience header (``). +I/O components are _not_ included by the global convenience header (``). [[rvariant.io.ostream]] === `operator<<` support [,cpp,subs="+macros,+attributes"] ---- -// +// #include -namespace temp_ns { +namespace iris { template constexpr bool pass:quotes[_ADL-ostreamable_] = {see-below}; // {exposition-only}pass:quotes[[.candidate\]#// 1#] @@ -1208,7 +1208,7 @@ constexpr bool pass:quotes[_ADL-ostreamable_] = {see-below}; // {exposition-only template std::ostream& operator<<(std::ostream& os, rvariant const& v);pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -1242,7 +1242,7 @@ std::ostream& operator<<(std::ostream& os, rvariant const& v);pass:quotes [none] * Let `v` denote an object of `rvariant`, and let `proxy` denote an object of `{variant-format-proxy}`. -* The specialization `std::formatter<::temp_ns::rvariant, charT>` (for arbitrary `charT`) is enabled if and only if `std::formattable<{unwrap_recursive_type}, charT>` is `true` for all _i_, with the following characteristics: +* The specialization `std::formatter<::iris::rvariant, charT>` (for arbitrary `charT`) is enabled if and only if `std::formattable<{unwrap_recursive_type}, charT>` is `true` for all _i_, with the following characteristics: + [none] ** -- The format specifier must be empty, otherwise `std::format_error` is thrown, and @@ -1252,7 +1252,7 @@ std::ostream& operator<<(std::ostream& os, rvariant const& v);pass:quotes + [,cpp,subs="+macros,+attributes"] ---- -std::println("{}", temp_ns::rvariant(42)); // prints pass:quotes[`42`] +std::println("{}", iris::rvariant(42)); // prints pass:quotes[`42`] ---- * The specialization `std::formatter<{variant-format-proxy}, charT>` is enabled if and only if: @@ -1272,10 +1272,10 @@ std::println("{}", temp_ns::rvariant(42)); // prints pass:quotes[`4 + [,cpp,subs="+macros,+attributes"] ---- -using V = temp_ns::rvariant; -constexpr auto v_fmt = temp_ns::variant_format_for("{:04d}", "{:.1f}"); -std::println("foo{}bar", temp_ns::format_by(v_fmt, V(42)); // prints pass:quotes[`foo0042bar`] -std::println("foo{}bar", temp_ns::format_by(v_fmt, V(3.14)); // prints pass:quotes[`foo3.1bar`] +using V = iris::rvariant; +constexpr auto v_fmt = iris::variant_format_for("{:04d}", "{:.1f}"); +std::println("foo{}bar", iris::format_by(v_fmt, V(42)); // prints pass:quotes[`foo0042bar`] +std::println("foo{}bar", iris::format_by(v_fmt, V(3.14)); // prints pass:quotes[`foo3.1bar`] ---- @@ -1291,7 +1291,7 @@ using pass:quotes[_select-char-t_] = {see-below}; // {exposition-only} [,cpp,subs="+macros,+attributes"] ---- -// +// #include @@ -1302,7 +1302,7 @@ struct {variant-format-string} // {exposition-only} auto const& operator()(std::in_place_type) const noexcept { return fmts...[i]; } }; -namespace temp_ns { +namespace iris { template constexpr {variant-format-string}<{see-below}> variant_format(Fmts&&... fmts) noexcept;pass:quotes[[.candidate\]#// 1#] @@ -1310,7 +1310,7 @@ constexpr {variant-format-string}<{see-below}> variant_format(Fmts&&... fmts) no template constexpr {variant-format-string}<{see-below}> variant_format_for(Fmts&&... fmts) noexcept;pass:quotes[[.candidate\]#// 2#] -} // temp_ns +} // iris ---- [.candidates] @@ -1329,7 +1329,7 @@ Let `charT` denote `_select-char-t_`. [,cpp,subs="+macros,+attributes"] ---- -// +// #include @@ -1340,13 +1340,13 @@ struct {variant-format-proxy} // {exposition-only} Variant v; }; -namespace temp_ns { +namespace iris { template constexpr {variant-format-proxy} format_by(VFormat&& v_fmt, Variant&& v) noexcept;pass:quotes[[.candidate\]#// 1#] -} // temp_ns +} // iris ---- [.candidates] @@ -1360,7 +1360,7 @@ format_by(VFormat&& v_fmt, Variant&& v) noexcept;pass:quotes[[.candidate\]#// 1# [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template class recursive_wrapper @@ -1368,7 +1368,7 @@ class recursive_wrapper {see-below} }; -} // temp_ns +} // iris ---- Class template `recursive_wrapper` behaves like `recursive_wrapper_alloca` instantiated with `std::allocator`, except that the allocator-related member functions (that is, member functions that take allocator-specific arguments) are omitted. @@ -1376,7 +1376,7 @@ Class template `recursive_wrapper` behaves like `recursive_wrapper_alloca` insta [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template> class recursive_wrapper_alloca @@ -1402,23 +1402,23 @@ template typename std::allocator_traits::template rebind_alloc >; -} // temp_ns +} // iris ---- [,cpp,subs="+macros,+attributes"] ---- -// +// #include -namespace temp_ns::pmr { +namespace iris::pmr { template -using recursive_wrapper = ::temp_ns::recursive_wrapper_alloca< +using recursive_wrapper = ::iris::recursive_wrapper_alloca< T, std::pmr::polymorphic_allocator >; -} // temp_ns::pmr +} // iris::pmr ---- [[rvariant.recursive.general]] @@ -1470,12 +1470,12 @@ _Note 2:_ It is currently unknown whether the recursive instantiation scenario d [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template using unwrap_recursive_type = {see-below}; -} // temp_ns +} // iris ---- [.candidates] @@ -1483,12 +1483,12 @@ using unwrap_recursive_type = {see-below}; [,cpp,subs="+macros,+attributes"] ---- -namespace temp_ns { +namespace iris { template constexpr auto&& unwrap_recursive(T&& o) noexcept; -} // temp_ns +} // iris ---- [.candidates] diff --git a/doc/rvariant.html b/doc/rvariant.html index a70e789..7103425 100644 --- a/doc/rvariant.html +++ b/doc/rvariant.html @@ -874,7 +874,7 @@

    rvariantReference [rvariant]

    @@ -1187,9 +1187,9 @@

    Advan abc = AB{}; // subset assignment } -static_assert(temp_ns::variant_size_v<AB> == 2); -static_assert(std::same_as<temp_ns::variant_alternative_t<0, AB>, A>); -static_assert(temp_ns::holds_alternative<A>(ab)); +static_assert(iris::variant_size_v<AB> == 2); +static_assert(std::same_as<iris::variant_alternative_t<0, AB>, A>); +static_assert(iris::holds_alternative<A>(ab)); static_assert(!ab.valueless_by_exception()); static_assert(ab.index() != std::variant_npos); @@ -1209,7 +1209,7 @@

    Advan // I/O support { - using V = ::temp_ns::rvariant<int, double>; + using V = ::iris::rvariant<int, double>; // operator<< support std::cout << V{42} << '\n'; // prints 42 @@ -1217,9 +1217,9 @@

    Advan // std::formatter support std::println("{}", V{42}); // prints 42 - constexpr auto v_fmt = temp_ns::variant_format_for<V>("{:04d}", "{:.1f}"); - std::println("foo{}bar", temp_ns::format_by(v_fmt, V(42)); // prints foo0042bar - std::println("foo{}bar", temp_ns::format_by(v_fmt, V(3.14)); // prints foo3.1bar + constexpr auto v_fmt = iris::variant_format_for<V>("{:04d}", "{:.1f}"); + std::println("foo{}bar", iris::format_by(v_fmt, V(42)); // prints foo0042bar + std::println("foo{}bar", iris::format_by(v_fmt, V(3.14)); // prints foo3.1bar } @@ -1239,7 +1239,7 @@

    General
    -

    Header <temp_ns/rvariant.hpp> synopsis [rvariant.syn]

    +

    Header <iris/rvariant.hpp> synopsis [rvariant.syn]

    @@ -1249,7 +1249,7 @@

    Header <temp #include <utility> // for std::in_place_type, etc. #include <variant> // for compatibility with std::bad_variant_access, etc. -namespace temp_ns { +namespace iris { // [rvariant.rvariant], class template rvariant template<class... Ts> @@ -1342,7 +1342,7 @@

    Header <temp template<class T> using unwrap_recursive_type = see below; template<class T> constexpr auto&& unwrap_recursive(T&& o) noexcept; -} // temp_ns +} // iris

    @@ -1350,10 +1350,10 @@

    Header <temp
    namespace std {
     
     // [rvariant.hash], hash support
    -template<class... Ts> struct hash<::temp_ns::rvariant<Ts...>>;
    +template<class... Ts> struct hash<::iris::rvariant<Ts...>>;
     
     // [rvariant.hash], hash support
    -template<class T, class Allocator> struct hash<::temp_ns::recursive_wrapper<T, Allocator>>;
    +template<class T, class Allocator> struct hash<::iris::recursive_wrapper<T, Allocator>>;
     
     } // std

    @@ -1365,7 +1365,7 @@

    Class
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class... Ts>
     class rvariant
    @@ -1440,7 +1440,7 @@ 

    Class constexpr R visit(this Self&&, Visitor&&); }; -} // temp_ns

    +} // iris
    @@ -2081,7 +2081,7 @@

    rva
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<std::size_t I, class T>
     struct variant_alternative; // not defined
    @@ -2092,7 +2092,7 @@ 

    rva template<std::size_t I, class... Ts> struct variant_alternative<I, rvariant<Ts...>>;// 2 -} // temp_ns

    +} // iris
    @@ -2116,7 +2116,7 @@

    Flexibility t
    -
    namespace temp_ns::rvariant_set {
    +
    namespace iris::rvariant_set {
     
     template<class W, class V>
     struct is_subset_of : std::false_type {};// 1
    @@ -2133,7 +2133,7 @@ 

    Flexibility t template<class W, class V> concept equivalent_to = subset_of<W, V> && subset_of<V, W>; -} // temp_ns::rvariant_set

    +} // iris::rvariant_set
    @@ -2163,12 +2163,12 @@

    Value access
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class T, class... Ts>
     constexpr bool holds_alternative(rvariant<Ts...> const& v) noexcept;
     
    -} // temp_ns
    +} // iris
    @@ -2214,7 +2214,7 @@

    Value access
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<std::size_t I, class... Ts>
     constexpr variant_alternative_t<I, rvariant<Ts...>>&
    @@ -2232,7 +2232,7 @@ 

    Value access

    +} // iris
    @@ -2247,14 +2247,14 @@

    Value access
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class T, class... Ts> constexpr T&        get(rvariant<Ts...>& v);
     template<class T, class... Ts> constexpr T&&       get(rvariant<Ts...>&& v);
     template<class T, class... Ts> constexpr T const&  get(rvariant<Ts...> const& v);
     template<class T, class... Ts> constexpr T const&& get(rvariant<Ts...> const&& v);
     
    -} // temp_ns
    +} // iris
    @@ -2272,7 +2272,7 @@

    Value access
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<std::size_t I, class... Ts>
     constexpr std::add_pointer_t<variant_alternative_t<I, rvariant<Ts...>>>
    @@ -2282,7 +2282,7 @@ 

    Value access // 2 -} // temp_ns

    +} // iris
    @@ -2297,7 +2297,7 @@

    Value access
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class T, class... Ts>
     constexpr std::add_pointer_t<T>
    @@ -2307,7 +2307,7 @@ 

    Value access // 2 -} // temp_ns

    +} // iris
    @@ -2330,7 +2330,7 @@

    Visitation
    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class Visitor, class... Variants>
     constexpr see below visit(Visitor&& vis, Variants&&... vars);// 1
    @@ -2338,7 +2338,7 @@ 

    Visitation template<class R, class Visitor, class... Variants> constexpr R visit(Visitor&& vis, Variants&&... vars);// 2 -} // temp_ns +} // iris // below are member functions of the class template rvariant: @@ -2362,7 +2362,7 @@

    Visitation

  • -

    3-4) Equivalent to the std::variant counterpart [spec], except that it forwards to temp_ns::visit instead of std::visit.

    +

    3-4) Equivalent to the std::variant counterpart [spec], except that it forwards to iris::visit instead of std::visit.

  • @@ -2376,13 +2376,13 @@

    Hash support
    namespace std {
     
     template<class... Ts>
    -struct hash<::temp_ns::rvariant<Ts...>>;// 1
    +struct hash<::iris::rvariant<Ts...>>;// 1
     
     template<class T>
    -struct hash<::temp_ns::recursive_wrapper<T>>;// 2
    +struct hash<::iris::recursive_wrapper<T>>;// 2
     
     template<class T, class Allocator>
    -struct hash<::temp_ns::recursive_wrapper_alloca<T, Allocator>>;// 3
    +struct hash<::iris::recursive_wrapper_alloca<T, Allocator>>;// 3
     
     } // std

    @@ -2409,7 +2409,7 @@

    Hash support

    -
    namespace temp_ns {
    +
    namespace iris {
     
     template<class... Ts>
     /* constexpr */ std::size_t hash_value(rvariant<Ts...> const& v);// 1
    @@ -2420,7 +2420,7 @@ 

    Hash support template<class T, class Allocator> /* constexpr */ std::size_t hash_value(recursive_wrapper_alloca<T, Allocator> const& rw);// 3 -} // temp_ns

    +} // iris
    @@ -2446,17 +2446,17 @@

    Hash support

    I/O [rvariant.io]

    -

    I/O components are not included by the global convenience header (<temp_ns/rvariant.hpp>).

    +

    I/O components are not included by the global convenience header (<iris/rvariant.hpp>).

    operator<< support

    -
    // <temp_ns/rvariant/rvariant_io.hpp>
    +
    // <iris/rvariant/rvariant_io.hpp>
     
     #include <ostream>
     
    -namespace temp_ns {
    +namespace iris {
     
     template<class T>
     constexpr bool ADL-ostreamable = see below; // exposition only// 1
    @@ -2464,7 +2464,7 @@ 

    < template<class... Ts> std::ostream& operator<<(std::ostream& os, rvariant<Ts...> const& v);// 2 -} // temp_ns

    +} // iris
    @@ -2522,7 +2522,7 @@

    Let v denote an object of rvariant, and let proxy denote an object of variant_format_proxy.

  • -

    The specialization std::formatter<::temp_ns::rvariant<Ts...>, charT> (for arbitrary charT) is enabled if and only if std::formattable<unwrap_recursive_type<Tsi>, charT> is true for all i, with the following characteristics:

    +

    The specialization std::formatter<::iris::rvariant<Ts...>, charT> (for arbitrary charT) is enabled if and only if std::formattable<unwrap_recursive_type<Tsi>, charT> is true for all i, with the following characteristics:

    • @@ -2538,7 +2538,7 @@

      Example:

      -
      std::println("{}", temp_ns::rvariant<int, double>(42)); // prints 42
      +
      std::println("{}", iris::rvariant<int, double>(42)); // prints 42

    • @@ -2578,10 +2578,10 @@

      Example:

      -
      using V = temp_ns::rvariant<int, double>;
      -constexpr auto v_fmt = temp_ns::variant_format_for<V>("{:04d}", "{:.1f}");
      -std::println("foo{}bar", temp_ns::format_by(v_fmt, V(42)); // prints foo0042bar
      -std::println("foo{}bar", temp_ns::format_by(v_fmt, V(3.14)); // prints foo3.1bar
      +
      using V = iris::rvariant<int, double>;
      +constexpr auto v_fmt = iris::variant_format_for<V>("{:04d}", "{:.1f}");
      +std::println("foo{}bar", iris::format_by(v_fmt, V(42)); // prints foo0042bar
      +std::println("foo{}bar", iris::format_by(v_fmt, V(3.14)); // prints foo3.1bar
      @@ -2605,7 +2605,7 @@

      -
      // <temp_ns/rvariant/rvariant_io.hpp>
      +
      // <iris/rvariant/rvariant_io.hpp>
       
       #include <format>
       
      @@ -2616,7 +2616,7 @@ 

      variant_format_string<see below> variant_format(Fmts&&... fmts) noexcept;// 1 @@ -2624,7 +2624,7 @@

      variant_format_string<see below> variant_format_for(Fmts&&... fmts) noexcept;// 2 -} // temp_ns

      +} // iris
      @@ -2651,7 +2651,7 @@

      -
      // <temp_ns/rvariant/rvariant_io.hpp>
      +
      // <iris/rvariant/rvariant_io.hpp>
       
       #include <format>
       
      @@ -2662,13 +2662,13 @@ 

      variant_format_proxy<VFormat, Variant> format_by(VFormat&& v_fmt, Variant&& v) noexcept;// 1 -} // temp_ns

      +} // iris
      @@ -2689,7 +2689,7 @@

      Cla
      -
      namespace temp_ns {
      +
      namespace iris {
       
       template<class T>
       class recursive_wrapper
      @@ -2697,7 +2697,7 @@ 

      Cla see below }; -} // temp_ns

      +} // iris
      @@ -2706,7 +2706,7 @@

      Cla

      -
      namespace temp_ns {
      +
      namespace iris {
       
       template<class T, class Allocator = std::allocator<T>>
       class recursive_wrapper_alloca
      @@ -2732,23 +2732,23 @@ 

      Cla typename std::allocator_traits<Allocator>::template rebind_alloc<Value> >; -} // temp_ns

      +} // iris
      -
      // <temp_ns/rvariant/recursive_wrapper_pmr.hpp>
      +
      // <iris/rvariant/recursive_wrapper_pmr.hpp>
       
       #include <memory_resource>
       
      -namespace temp_ns::pmr {
      +namespace iris::pmr {
       
       template<class T>
      -using recursive_wrapper = ::temp_ns::recursive_wrapper_alloca<
      +using recursive_wrapper = ::iris::recursive_wrapper_alloca<
         T, std::pmr::polymorphic_allocator<T>
       >;
       
      -} // temp_ns::pmr
      +} // iris::pmr
      @@ -2841,12 +2841,12 @@

      -
      namespace temp_ns {
      +
      namespace iris {
       
       template<class T>
       using unwrap_recursive_type = see below;
       
      -} // temp_ns
      +} // iris
      From f47046a9422f11cd8d746675f0bd3811284b57ef Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:35:32 +0900 Subject: [PATCH 09/10] Remove `#include` from spec --- doc/rvariant.adoc | 14 -------------- doc/rvariant.html | 16 +--------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/doc/rvariant.adoc b/doc/rvariant.adoc index 7a94b47..f5838cb 100644 --- a/doc/rvariant.adoc +++ b/doc/rvariant.adoc @@ -289,12 +289,6 @@ std::size_t _ = <>(ab); // compatibility with boost [,cpp,subs="+macros,+attributes"] ---- -#include -#include // for pass:quotes[`std::allocator`], etc. -#include // for pass:quotes[`std::add_pointer`], etc. -#include // for pass:quotes[`std::in_place_type`], etc. -#include // for compatibility with {bad-variant-access}, etc. - namespace iris { // <>, class template pass:quotes[`rvariant`] @@ -1198,8 +1192,6 @@ I/O components are _not_ included by the global convenience header (` -#include - namespace iris { template @@ -1293,8 +1285,6 @@ using pass:quotes[_select-char-t_] = {see-below}; // {exposition-only} ---- // -#include - template struct {variant-format-string} // {exposition-only} { @@ -1331,8 +1321,6 @@ Let `charT` denote `_select-char-t_`. ---- // -#include - template struct {variant-format-proxy} // {exposition-only} { @@ -1409,8 +1397,6 @@ template ---- // -#include - namespace iris::pmr { template diff --git a/doc/rvariant.html b/doc/rvariant.html index 7103425..e7f46ac 100644 --- a/doc/rvariant.html +++ b/doc/rvariant.html @@ -1243,13 +1243,7 @@

      Header <iris
      -
      #include <compare>
      -#include <memory>      // for std::allocator, etc.
      -#include <type_traits> // for std::add_pointer, etc.
      -#include <utility>     // for std::in_place_type, etc.
      -#include <variant>     // for compatibility with std::bad_variant_access, etc.
      -
      -namespace iris {
      +
      namespace iris {
       
       // [rvariant.rvariant], class template rvariant
       template<class... Ts>
      @@ -2454,8 +2448,6 @@ 

      <
      // <iris/rvariant/rvariant_io.hpp>
       
      -#include <ostream>
      -
       namespace iris {
       
       template<class T>
      @@ -2607,8 +2599,6 @@ 

      // <iris/rvariant/rvariant_io.hpp>
       
      -#include <format>
      -
       template<class charT, class... Ts>
       struct variant_format_string // exposition only
       {
      @@ -2653,8 +2643,6 @@ 

      // <iris/rvariant/rvariant_io.hpp>
       
      -#include <format>
      -
       template<class VFormat, class Variant>
       struct variant_format_proxy // exposition only
       {
      @@ -2739,8 +2727,6 @@ 

      Cla
      // <iris/rvariant/recursive_wrapper_pmr.hpp>
       
      -#include <memory_resource>
      -
       namespace iris::pmr {
       
       template<class T>
      
      From e61d242b00daf73830a2a478f588520a8957ad22 Mon Sep 17 00:00:00 2001
      From: Nana Sakisaka <1901813+saki7@users.noreply.github.com>
      Date: Tue, 3 Mar 2026 10:50:10 +0900
      Subject: [PATCH 10/10] Refactor indirect and recursive wrapper
      
      ---
       include/iris/indirect.hpp                   | 45 ++++++++++-----------
       include/iris/rvariant/recursive_wrapper.hpp | 18 ++++++++-
       2 files changed, 38 insertions(+), 25 deletions(-)
      
      diff --git a/include/iris/indirect.hpp b/include/iris/indirect.hpp
      index 0d977c1..c5084cc 100644
      --- a/include/iris/indirect.hpp
      +++ b/include/iris/indirect.hpp
      @@ -3,9 +3,9 @@
       
       // SPDX-License-Identifier: MIT
       
      +#include 
       #include 
       #include 
      -#include 
       
       #include 
       #include 
      @@ -25,13 +25,12 @@ class scoped_allocation
           using pointer = std::allocator_traits::pointer;
           using const_pointer = std::allocator_traits::const_pointer;
       
      -
           template
      -    constexpr explicit scoped_allocation(Alloc const& a, std::in_place_t, Args&&... args)
      +    constexpr explicit scoped_allocation(Alloc const& a, Args&&... args)
               : alloc_(a)
               , ptr_(std::allocator_traits::allocate(alloc_, 1))
           {
      -        std::allocator_traits::construct(alloc_, get(), std::forward(args)...);
      +        std::allocator_traits::construct(alloc_, this->get(), std::forward(args)...);
           }
       
           constexpr ~scoped_allocation()
      @@ -70,7 +69,7 @@ class indirect_base
           using const_pointer = std::allocator_traits::const_pointer;
       
           constexpr explicit indirect_base() requires std::is_default_constructible_v
      -        : ptr_(make_obj())
      +        : ptr_(this->make_obj())
           {}
       
           constexpr indirect_base(indirect_base const& other)
      @@ -83,12 +82,12 @@ class indirect_base
       
           constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a)
               : alloc_(a)
      -        , ptr_(make_obj())
      +        , ptr_(this->make_obj())
           {}
       
           constexpr indirect_base(std::allocator_arg_t, Allocator const& a, indirect_base const& other)
               : alloc_(a)
      -        , ptr_(other.ptr_ ? make_obj(std::as_const(*other.ptr_)) : nullptr)
      +        , ptr_(other.ptr_ ? this->make_obj(std::as_const(*other.ptr_)) : nullptr)
           {
               static_assert(std::is_copy_constructible_v);
           }
      @@ -103,7 +102,7 @@ class indirect_base
               : alloc_(a)
               , ptr_(alloc_ == other.alloc_
                   ? std::exchange(other.ptr_, nullptr)
      -            : make_obj(*std::move(other))
      +            : this->make_obj(*std::move(other))
               )
           {}
       
      @@ -114,7 +113,7 @@ class indirect_base
                   std::is_constructible_v &&
                   std::is_default_constructible_v
           constexpr explicit indirect_base(U&& u)
      -        : ptr_(make_obj(std::forward(u)))
      +        : ptr_(this->make_obj(std::forward(u)))
           {}
       
           template
      @@ -124,7 +123,7 @@ class indirect_base
                   std::is_constructible_v
           constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a, U&& u)
               : alloc_(a)
      -        , ptr_(make_obj(std::forward(u)))
      +        , ptr_(this->make_obj(std::forward(u)))
           {}
       
           template
      @@ -132,7 +131,7 @@ class indirect_base
                   std::is_constructible_v &&
                   std::is_default_constructible_v
           constexpr explicit indirect_base(std::in_place_t, Us&&... us)
      -        : ptr_(make_obj(std::forward(us)...))
      +        : ptr_(this->make_obj(std::forward(us)...))
           {}
       
           template
      @@ -140,7 +139,7 @@ class indirect_base
                   std::is_constructible_v
           constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a, std::in_place_t, Us&&... us)
               : alloc_(a)
      -        , ptr_(make_obj(std::forward(us)...))
      +        , ptr_(this->make_obj(std::forward(us)...))
           {}
       
           template
      @@ -148,7 +147,7 @@ class indirect_base
                   std::is_constructible_v&, Us...> &&
                   std::is_default_constructible_v
           constexpr explicit indirect_base(std::in_place_t, std::initializer_list il, Us&&... us)
      -        : ptr_(make_obj(il, std::forward(us)...))
      +        : ptr_(this->make_obj(il, std::forward(us)...))
           {}
       
           template
      @@ -156,13 +155,13 @@ class indirect_base
                   std::is_constructible_v&, Us...>
           constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a, std::in_place_t, std::initializer_list il, Us&&... us)
               : alloc_(a)
      -        , ptr_(make_obj(il, std::forward(us)...))
      +        , ptr_(this->make_obj(il, std::forward(us)...))
           {}
       
           constexpr ~indirect_base() noexcept
           {
               if (ptr_) [[likely]] {
      -            destroy_deallocate();
      +            this->destroy_deallocate();
               }
           }
       
      @@ -220,7 +219,7 @@ class indirect_base
                       // other.ptr_ && (ptr_ || !ptr_)
                       // either allocator is not equal or `this` does not contain value; create copy from `other`
                       if (ptr_) [[likely]] {
      -                    destroy_deallocate();
      +                    this->destroy_deallocate();
                           ptr_ = nullptr; // make it safer
                       }
                       if constexpr (pocca) {
      @@ -234,7 +233,7 @@ class indirect_base
       
               } else [[unlikely]] { // !other.ptr_
                   if (ptr_) [[likely]] {
      -                destroy_deallocate();
      +                this->destroy_deallocate();
                       ptr_ = nullptr; // make it safer
                   }
                   if constexpr (pocca) {
      @@ -261,7 +260,7 @@ class indirect_base
               if (other.ptr_) [[likely]] {
                   if constexpr (std::allocator_traits::is_always_equal::value) {
                       if (ptr_) [[likely]] {
      -                    destroy_deallocate();
      +                    this->destroy_deallocate();
                       }
                       ptr_ = std::exchange(other.ptr_, nullptr);
                       if constexpr (pocma) {
      @@ -271,7 +270,7 @@ class indirect_base
       
                   } else if (alloc_ == other.alloc_) {
                       if (ptr_) [[likely]] {
      -                    destroy_deallocate();
      +                    this->destroy_deallocate();
                       }
                       ptr_ = std::exchange(other.ptr_, nullptr);
                       if constexpr (pocma) {
      @@ -281,7 +280,7 @@ class indirect_base
       
                   } else {
                       if (ptr_) [[likely]] {
      -                    destroy_deallocate();
      +                    this->destroy_deallocate();
                       }
                       if constexpr (pocma) {
                           ptr_ = other.make_obj(std::move(*other));
      @@ -293,7 +292,7 @@ class indirect_base
                   }
               } else [[unlikely]] { // !other.ptr_
                   if (ptr_) [[likely]] {
      -                destroy_deallocate();
      +                this->destroy_deallocate();
                       ptr_ = nullptr;
                   }
                   if constexpr (pocma) {
      @@ -314,7 +313,7 @@ class indirect_base
                   **this = std::forward(u);
       
               } else [[unlikely]] {
      -            ptr_ = make_obj(std::forward(u));
      +            ptr_ = this->make_obj(std::forward(u));
               }
               return *this;
           }
      @@ -361,7 +360,7 @@ class indirect_base
           template
           [[nodiscard]] constexpr T* make_obj(Args&&... args) const
           {
      -        detail::scoped_allocation sa(alloc_, std::in_place, std::forward(args)...);
      +        detail::scoped_allocation sa(alloc_, std::forward(args)...);
               return sa.release();
           }
       
      diff --git a/include/iris/rvariant/recursive_wrapper.hpp b/include/iris/rvariant/recursive_wrapper.hpp
      index daa0676..e2da0a3 100644
      --- a/include/iris/rvariant/recursive_wrapper.hpp
      +++ b/include/iris/rvariant/recursive_wrapper.hpp
      @@ -3,6 +3,8 @@
       
       // SPDX-License-Identifier: MIT
       
      +#include 
      +
       #include 
       #include 
       
      @@ -13,13 +15,22 @@
       
       namespace iris {
       
      +template
      +class recursive_wrapper;
      +
      +template
      +class recursive_wrapper_alloca;
      +
       // recursive_wrapper (fixed to `std::allocator`)
       // This class covers 99.99% of recursive-variant use cases.
       template
       class recursive_wrapper
           : private detail::indirect_base>
       {
      -    static_assert(!detail::is_recursive_wrapper_like_v, "recursive wrapper of recursive wrapper is not allowed");
      +    static_assert(
      +        !is_ttp_specialization_of_v && !is_ttp_specialization_of_v,
      +        "recursive wrapper of recursive wrapper is not allowed"
      +    );
       
           // Note: this implementation is copied from `recursive_wrapper_alloca` below.
           // If any changes are required, modify `recursive_wrapper_alloca` first.
      @@ -116,7 +127,10 @@ template>
       class recursive_wrapper_alloca
           : private detail::indirect_base
       {
      -    static_assert(!detail::is_recursive_wrapper_like_v, "recursive wrapper of recursive wrapper is not allowed");
      +    static_assert(
      +        !is_ttp_specialization_of_v && !is_ttp_specialization_of_v,
      +        "recursive wrapper of recursive wrapper is not allowed"
      +    );
       
           using base_type = detail::indirect_base;