diff --git a/doc/.gitattributes b/doc/.gitattributes new file mode 100644 index 0000000..0334717 --- /dev/null +++ b/doc/.gitattributes @@ -0,0 +1 @@ +*.html linguist-vendored diff --git a/doc/rvariant.adoc b/doc/rvariant.adoc index f5838cb..09fac6c 100644 --- a/doc/rvariant.adoc +++ b/doc/rvariant.adoc @@ -22,7 +22,7 @@ Yaito Kakeyama; Nana Sakisaka :exposition-only: pass:quotes[[.exposition-only]#exposition only#] :bad-variant-access: pass:quotes,macros[https://eel.is/c++draft/variant.bad.access[`std::bad_variant_access`]] -:unwrap_recursive_type: <> +:unwrap_recursive_t: <> :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]] @@ -50,9 +50,9 @@ expr.visit(iris::overloaded{ [discrete] === Supported Environments -* GCC 14 -* Clang 21-22 (lib{cxx}) -* MSVC 2022 and 2026 +* GCC 15 +* Clang 22 (lib{cxx}) +* MSVC 2026 * {cpp}23 and {cpp}26 @@ -379,7 +379,7 @@ template /* constexpr */ std::size_t hash_value(recursive_wrapper_alloca const&); // <>, pass:quotes[`recursive_wrapper`] helper classes -template using unwrap_recursive_type = {see-below}; +template using unwrap_recursive_t = {see-below}; template constexpr auto&& unwrap_recursive(T&& o) noexcept; } // iris @@ -508,7 +508,7 @@ rvariant< ---- -- -* 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`#. +* 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_t}, {unwrap_recursive_t}>` 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`. @@ -555,7 +555,7 @@ include::_std-variant-proxy.adoc[] * [.candidate]#4)# _Generic constructor_. Equivalent to the `std::variant` counterpart, ^https://eel.is/c++draft/variant.ctor[[spec\]]^ except: + -*_Postconditions:_* `holds_alternative[.underline]##<{unwrap_recursive_type}>##(*this)` is `true`. +*_Postconditions:_* `holds_alternative[.underline]##<{unwrap_recursive_t}>##(*this)` is `true`. * [.candidate]#5)# *_Mandates:_* [.underline]#`T` is not a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`#. + @@ -563,7 +563,7 @@ include::_std-variant-proxy.adoc[] + -- [none] -** -- There is exactly one occurrence of `T` in [.underline]#`{unwrap_recursive_type}\...`# and +** -- There is exactly one occurrence of `T` in [.underline]#`{unwrap_recursive_t}\...`# and ** -- `std::is_constructible_v<[.underline]##VT##, Args\...>` is `true`. -- + @@ -581,7 +581,7 @@ include::_std-variant-proxy.adoc[] + -- [none] -** -- There is exactly one occurrence of `T` in [.underline]#`{unwrap_recursive_type}\...`# and +** -- There is exactly one occurrence of `T` in [.underline]#`{unwrap_recursive_t}\...`# and ** -- `std::is_constructible_v<[.underline]##VT##, std::initializer_list&, Args\...>` is `true`. -- + @@ -606,7 +606,7 @@ include::_std-variant-proxy.adoc[] [none] ** -- `std::is_same_v, rvariant>` is `false`, and ** -- [.underline]#`{subset_of}, rvariant>` is `true`, and# -** -- [.underline]#`std::disjunction_v, {unwrap_recursive_type}>\...>` is `false`, and# +** -- [.underline]#`std::disjunction_v, {unwrap_recursive_t}>\...>` is `false`, and# ** -- [.underline]#`std::is_constructible_v` is `true` for all _j_.# -- + @@ -630,7 +630,7 @@ include::_std-variant-proxy.adoc[] [none] ** -- `std::is_same_v, rvariant>` is `false`, and ** -- `{subset_of}, rvariant>` is `true`, and -** -- `std::disjunction_v, {unwrap_recursive_type}>\...>` is `false`, and +** -- `std::disjunction_v, {unwrap_recursive_t}>\...>` is `false`, and ** -- `std::is_constructible_v` is `true` for all _j_. -- + @@ -688,7 +688,7 @@ include::_std-variant-proxy.adoc[] * [.candidate]#3)# _Generic assignment operator_. Equivalent to the `std::variant` counterpart, ^https://eel.is/c++draft/variant.assign[[spec\]]^ except: + -*_Postconditions:_* `holds_alternative[.underline]##<{unwrap_recursive_type}>##(*this)` is `true`, with `T~_j_~` selected by the imaginary function overload resolution described above. +*_Postconditions:_* `holds_alternative[.underline]##<{unwrap_recursive_t}>##(*this)` is `true`, with `T~_j_~` selected by the imaginary function overload resolution described above. * [.candidate]#4)# _Flexible copy assignment operator_. + @@ -700,7 +700,7 @@ include::_std-variant-proxy.adoc[] [none] ** -- `std::is_same_v, rvariant>` is `false`, and ** -- [.underline]#`{subset_of}, rvariant>` is `true`, and# -** -- [.underline]#`std::disjunction_v, {unwrap_recursive_type}>\...>` is `false`, and# +** -- [.underline]#`std::disjunction_v, {unwrap_recursive_t}>\...>` is `false`, and# ** -- [.underline]#`std::is_constructible_v && std::is_assignable_v` is `true` for all _j_.# -- + @@ -711,7 +711,7 @@ include::_std-variant-proxy.adoc[] ** -- If neither `*this` nor `rhs` holds a value, there is no effect. ** -- Otherwise, if `*this` holds a value but `rhs` does not, destroys the value contained in `*this` and sets `*this` to not hold a value. ** -- Otherwise, if `rhs` holds a value but `*this` does not, initializes `rvariant` to hold [.underline]#`VT~_i_~` (with _i_ being the index of the alternative corresponding to that of `rhs`)# and direct-initializes the contained value with [.underline]#`_GET_<__j__>(rhs)`.# -** -- Otherwise, if [.underline]#`std::is_same_v<{unwrap_recursive_type}, {unwrap_recursive_type}>` is `true`#, assigns [.underline]#`_GET_<__j__>(rhs)`# to the value contained in `*this`. [.underline]#(_Note:_ the left hand side is `T~_i_~`, _not_ `VT~_i_~`. This ensures that the existing storage is reused even for `rvariant` with duplicate corresponding alternatives; i.e., `index()` is unchanged.)# +** -- Otherwise, if [.underline]#`std::is_same_v<{unwrap_recursive_t}, {unwrap_recursive_t}>` is `true`#, assigns [.underline]#`_GET_<__j__>(rhs)`# to the value contained in `*this`. [.underline]#(_Note:_ the left hand side is `T~_i_~`, _not_ `VT~_i_~`. This ensures that the existing storage is reused even for `rvariant` with duplicate corresponding alternatives; i.e., `index()` is unchanged.)# ** -- Otherwise, if either `std::is_nothrow_constructible_v` is `true` or `std::is_nothrow_move_constructible_v` is `false`, equivalent to `emplace[.underline]####(_GET_<__j__>(rhs))`. ** -- Otherwise, equivalent to `emplace([.underline]##VT~_i_~##(_GET_<__j__>(rhs)))`. -- @@ -732,7 +732,7 @@ include::_std-variant-proxy.adoc[] [none] ** -- [.underline]#`std::is_same_v, rvariant>` is `false`,# ** -- [.underline]#`{subset_of}, rvariant>` is `true`, and# -** -- [.underline]#`std::disjunction_v, {unwrap_recursive_type}>\...>` is `false`, and# +** -- [.underline]#`std::disjunction_v, {unwrap_recursive_t}>\...>` is `false`, and# ** -- [.underline]#`std::is_constructible_v && std::is_assignable_v` is `true` for all _j_.# -- + @@ -743,7 +743,7 @@ include::_std-variant-proxy.adoc[] ** -- If neither `*this` nor `rhs` holds a value, there is no effect. ** -- Otherwise, if `*this` holds a value but `rhs` does not, destroys the value contained in `*this` and sets `*this` to not hold a value. ** -- Otherwise, if `rhs` holds a value but `*this` does not, initializes `rvariant` to hold [.underline]#`VT~_i_~` (with _i_ being the index of the alternative corresponding to that of `rhs`)# and direct-initializes the contained value with [.underline]#`_GET_<__j__>(std::move(rhs))`.# -** -- Otherwise, if [.underline]#`std::is_same_v<{unwrap_recursive_type}, {unwrap_recursive_type}>` is `true`#, assigns [.underline]#`_GET_<__j__>(std::move(rhs))`# to the value contained in `*this`. [.underline]#(_Note:_ the left hand side is `T~_i_~`, _not_ `VT~_i_~`. This ensures that the existing storage is reused even for `rvariant` with duplicate corresponding alternatives; i.e., `index()` is unchanged.)# +** -- Otherwise, if [.underline]#`std::is_same_v<{unwrap_recursive_t}, {unwrap_recursive_t}>` is `true`#, assigns [.underline]#`_GET_<__j__>(std::move(rhs))`# to the value contained in `*this`. [.underline]#(_Note:_ the left hand side is `T~_i_~`, _not_ `VT~_i_~`. This ensures that the existing storage is reused even for `rvariant` with duplicate corresponding alternatives; i.e., `index()` is unchanged.)# ** -- Otherwise, equivalent to `emplace[.underline]####(_GET_<__j__>(std::move(rhs)))`. -- + @@ -777,19 +777,19 @@ constexpr variant_alternative_t>& * [.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}`#. +*_Constraints:_* `std::is_constructible_v<[.underline]##VT##, Args\...>` is `true`, and `T` occurs exactly once in [.underline]#`{unwrap_recursive_t}`#. + *_Effects:_* Equivalent to: + pass:quotes[  ]`return emplace<__I__>(std::forward(args)\...);` + -where `_I_` is the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. +where `_I_` is the zero-based index of `T` in [.underline]#`{unwrap_recursive_t}`#. * [.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}`#. +*_Constraints:_* `std::is_constructible_v<[.underline]##VT##, std::initializer_list&, Args\...>` is `true`, and `T` occurs exactly once in [.underline]#`{unwrap_recursive_t}`#. + *_Effects:_* Equivalent to: + pass:quotes[  ]`return emplace<__I__>(il, std::forward(args)\...);` + -where `_I_` is the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. +where `_I_` is the zero-based index of `T` in [.underline]#`{unwrap_recursive_t}`#. * [.candidate]#3)# Equivalent to the `std::variant` counterpart, ^https://eel.is/c++draft/variant.mod[[spec\]]^ except: + @@ -915,7 +915,7 @@ struct variant_alternative>;pass:quotes[[.candidate\]#// 2#] * [.candidate]#1)# include::_std-variant-proxy.adoc[] -* [.candidate]#2)# The member typedef `type` denotes [.underline]#`{unwrap_recursive_type}`#. +* [.candidate]#2)# The member typedef `type` denotes [.underline]#`{unwrap_recursive_t}`#. + *_Mandates:_* `I < sizeof\...(Ts)`. @@ -952,7 +952,7 @@ concept equivalent_to = subset_of && subset_of; + [none] ** -- `T` is the same type as `U` or, -** -- `{unwrap_recursive_type}` is the same type as `U`. +** -- `{unwrap_recursive_t}` is the same type as `U`. [[rvariant.get]] @@ -969,9 +969,9 @@ constexpr bool holds_alternative(rvariant const& v) noexcept; ---- [.candidates] -* [.candidate]#{empty}# *_Mandates:_* The type `T` occurs exactly once in [.underline]#`{unwrap_recursive_type}`#. +* [.candidate]#{empty}# *_Mandates:_* The type `T` occurs exactly once in [.underline]#`{unwrap_recursive_t}`#. + -*_Returns:_* `true` if `v.index()` is equal to the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. +*_Returns:_* `true` if `v.index()` is equal to the zero-based index of `T` in [.underline]#`{unwrap_recursive_t}`#. + *_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# @@ -1041,9 +1041,9 @@ template constexpr T const&& get(rvariant const&& v ---- [.candidates] -* [.candidate]#{empty}# *_Mandates:_* The type `T` occurs exactly once in [.underline]#`{unwrap_recursive_type}`#. +* [.candidate]#{empty}# *_Mandates:_* The type `T` occurs exactly once in [.underline]#`{unwrap_recursive_t}`#. + -*_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}. +*_Effects:_* [.underline]#Let `VT` denote the type of the alternative held by `v`. If `{unwrap_recursive_t}` 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}` or `{recursive_wrapper_alloca}`.# @@ -1085,9 +1085,9 @@ constexpr std::add_pointer_t ---- [.candidates] -* [.candidate]#1-2)# *_Mandates:_* The type `T` occurs exactly once in [.underline]#`{unwrap_recursive_type}`#. +* [.candidate]#1-2)# *_Mandates:_* The type `T` occurs exactly once in [.underline]#`{unwrap_recursive_t}`#. + -*_Effects:_* Equivalent to: `return get_if<__i__>(v);` with _i_ being the zero-based index of `T` in [.underline]#`{unwrap_recursive_type}`#. +*_Effects:_* Equivalent to: `return get_if<__i__>(v);` with _i_ being the zero-based index of `T` in [.underline]#`{unwrap_recursive_t}`#. + *_Remarks:_* [.underline]#This function is defined as deleted if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`.# @@ -1210,7 +1210,7 @@ std::ostream& operator<<(std::ostream& os, rvariant const& v);pass:quotes ** -- the expression `os << val` is well-formed and has the type `std::ostream&`, and ** -- the corresponding overload is found solely via ADL. -* [.candidate]#2)# *_Constraints:_* `_ADL-ostreamable_<{unwrap_recursive_type}>` is `true` for all _i_. +* [.candidate]#2)# *_Constraints:_* `_ADL-ostreamable_<{unwrap_recursive_t}>` is `true` for all _i_. + *_Effects:_* Behaves as a formatted output function (https://eel.is/c++draft/ostream.formatted.reqmts[[ostream.formatted.reqmts\]]) of `os`, except that: + @@ -1234,7 +1234,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<::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: +* The specialization `std::formatter<::iris::rvariant, charT>` (for arbitrary `charT`) is enabled if and only if `std::formattable<{unwrap_recursive_t}, 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::println("{}", iris::rvariant(42)); // prints pass:quotes[`42`] [none] ** -- `std::remove_cvref_t` is a specialization of `{variant-format-string}`, and ** -- `std::remove_cvref_t` is a specialization of `rvariant`, and -** -- `std::formattable<{unwrap_recursive_type}, charT>` is `true` for all _i_, with `Ts` being the template parameter pack of cv-unqualified non-reference type for `Variant`. +** -- `std::formattable<{unwrap_recursive_t}, charT>` is `true` for all _i_, with `Ts` being the template parameter pack of cv-unqualified non-reference type for `Variant`. * It has the following characteristics: + @@ -1459,13 +1459,14 @@ _Note 2:_ It is currently unknown whether the recursive instantiation scenario d namespace iris { template -using unwrap_recursive_type = {see-below}; +using unwrap_recursive_t = {see-below}; } // iris ---- [.candidates] -* [.candidate]#{empty}# Denotes `T::value_type` if `T` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`. Otherwise, denotes `T`. +* [.candidate]#{empty}# Let `U` be `std::remove_cvref_t`, and let `V` be `U::value_type` if `U` is a specialization of `{recursive_wrapper}` or `{recursive_wrapper_alloca}`, and `U` otherwise. Denotes the type obtained from `T` by replacing `U` with `V`, preserving the const-qualification and the reference (if any) of `T`. +* [.candidate]#{empty}# *Note:* For example, `unwrap_recursive_t<{recursive_wrapper} const&>` denotes `X const&`. If `T` is a reference type, `unwrap_recursive_t` denotes the same type as `decltype({unwrap_recursive}(std::declval()))`. If `T` is not a reference type, `unwrap_recursive_t` is also not a reference type, so that it can be further qualified (e.g. `unwrap_recursive_t const&`). [,cpp,subs="+macros,+attributes"] ---- diff --git a/doc/rvariant.html b/doc/rvariant.html index e7f46ac..8418c98 100644 --- a/doc/rvariant.html +++ b/doc/rvariant.html @@ -4,10 +4,9 @@ - -rvariant - +rvariant<span class="image"><img src="https://img.shields.io/badge/GitHub-iris-purple?logo=github" alt="GitHub"></span></a> + + @@ -850,15 +850,15 @@

rvariant
rvariant
-
    -
  • Introduction +
      +
    • Introduction
    • -
    • Rationale +
    • Rationale
      • Why is recursive_wrapper needed?
      • Why can’t I just use std::unique_ptr instead of recursive_wrapper?
      • @@ -924,7 +924,7 @@

        rvariant
        -
        // A common pattern for representing recursive ASTs using recursive variants.
        +
        // A common pattern for representing recursive ASTs using recursive variants.
         struct BinaryExpr;
         using Expr = iris::rvariant<int, double, iris::recursive_wrapper<BinaryExpr>>;
         enum class Op;
        @@ -942,13 +942,13 @@ 

        Supported Environments

        • -

          GCC 14

          +

          GCC 15

        • -

          Clang 21-22 (libc++)

          +

          Clang 22 (libc++)

        • -

          MSVC 2022 and 2026

          +

          MSVC 2026

        • C++23 and C++26

          @@ -997,11 +997,11 @@

          Comparison of Varia
          -----+++++ @@ -1123,7 +1123,7 @@

          Basic Featu
          -
          using A = int;
          +
          using A = int;
           using B = double;
           struct C {};
           
          @@ -1177,7 +1177,7 @@ 

          Advan
          -
          {
          +
          {
               // flexible construction
               AB ab{BA{}};   // unordered construction
               ABC abc{AB{}}; // subset construction
          @@ -1243,7 +1243,7 @@ 

          Header <iris
          -
          namespace iris {
          +
          namespace iris {
           
           // [rvariant.rvariant], class template rvariant
           template<class... Ts>
          @@ -1333,7 +1333,7 @@ 

          Header <iris /* 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; +template<class T> using unwrap_recursive_t = see below; template<class T> constexpr auto&& unwrap_recursive(T&& o) noexcept; } // iris

          @@ -1341,7 +1341,7 @@

          Header <iris

          -
          namespace std {
          +
          namespace std {
           
           // [rvariant.hash], hash support
           template<class... Ts> struct hash<::iris::rvariant<Ts...>>;
          @@ -1359,7 +1359,7 @@ 

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

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

          • -

            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.

            +

            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_t<VTi>, unwrap_recursive_t<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.

            @@ -1503,7 +1503,7 @@

            Constructors [rvariant.ctor]

            -
            constexpr rvariant::rvariant() noexcept(see below);// 1
            +
            constexpr rvariant::rvariant() noexcept(see below);// 1
             constexpr rvariant::rvariant(rvariant const& w);// 2
             constexpr rvariant::rvariant(rvariant&& w) noexcept(see below);// 3
             
            @@ -1543,7 +1543,7 @@ 

            Constructors
          • 4) Generic constructor. Equivalent to the std::variant counterpart, [spec] except:

            -

            Postconditions: holds_alternative<unwrap_recursive_type<Tj>>(*this) is true.

            +

            Postconditions: holds_alternative<unwrap_recursive_t<Tj>>(*this) is true.

          • @@ -1556,7 +1556,7 @@

            Constructors
            • -

               — There is exactly one occurrence of T in unwrap_recursive_type<Ts>... and

              +

               — There is exactly one occurrence of T in unwrap_recursive_t<Ts>... and

            •  — std::is_constructible_v<VT, Args...> is true.

              @@ -1588,7 +1588,7 @@

              Constructors
              • -

                 — There is exactly one occurrence of T in unwrap_recursive_type<Ts>... and

                +

                 — There is exactly one occurrence of T in unwrap_recursive_t<Ts>... and

              •  — std::is_constructible_v<VT, std::initializer_list<U>&, Args...> is true.

                @@ -1633,7 +1633,7 @@

                Constructors

                 — rvariant_set::subset_of<rvariant<Us...>, rvariant> is true, and

              • -

                 — std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_type<Ts>>...> is false, and

                +

                 — std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_t<Ts>>...> is false, and

              •  — std::is_constructible_v<VTi, Uj const&> is true for all j.

                @@ -1678,7 +1678,7 @@

                Constructors

                 — rvariant_set::subset_of<rvariant<Us...>, rvariant> is true, and

              • -

                 — std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_type<Ts>>...> is false, and

                +

                 — std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_t<Ts>>...> is false, and

              •  — std::is_constructible_v<VTi, Uj&&> is true for all j.

                @@ -1711,7 +1711,7 @@

                Constructors

                Destructor [rvariant.dtor]

                -
                constexpr ~rvariant();// 1
                +
                constexpr ~rvariant();// 1
                @@ -1731,7 +1731,7 @@

                Destructor Assignment [rvariant.assign]

                -
                constexpr rvariant& operator=(rvariant const& rhs);// 1
                +
                constexpr rvariant& operator=(rvariant const& rhs);// 1
                 constexpr rvariant& operator=(rvariant&& rhs) noexcept(see below);// 2
                 
                 // Generic assignment operator
                @@ -1756,7 +1756,7 @@ 

                Assignmen
              • 3) Generic assignment operator. Equivalent to the std::variant counterpart, [spec] except:

                -

                Postconditions: holds_alternative<unwrap_recursive_type<Tj>>(*this) is true, with Tj selected by the imaginary function overload resolution described above.

                +

                Postconditions: holds_alternative<unwrap_recursive_t<Tj>>(*this) is true, with Tj selected by the imaginary function overload resolution described above.

              • @@ -1778,7 +1778,7 @@

                Assignmen

                 — rvariant_set::subset_of<rvariant<Us...>, rvariant> is true, and

              • -

                 — std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_type<Ts>>...> is false, and

                +

                 — std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_t<Ts>>...> is false, and

              •  — std::is_constructible_v<VTi, Uj const&> && std::is_assignable_v<VTi&, Uj const&> is true for all j.

                @@ -1804,7 +1804,7 @@

                Assignmen

                 — Otherwise, if rhs holds a value but *this does not, initializes rvariant to hold VTi (with i being the index of the alternative corresponding to that of rhs) and direct-initializes the contained value with GET<j>(rhs).

              • -

                 — Otherwise, if std::is_same_v<unwrap_recursive_type<Ti>, unwrap_recursive_type<Uj>> is true, assigns GET<j>(rhs) to the value contained in *this. (Note: the left hand side is Ti, not VTi. This ensures that the existing storage is reused even for rvariant with duplicate corresponding alternatives; i.e., index() is unchanged.)

                +

                 — Otherwise, if std::is_same_v<unwrap_recursive_t<Ti>, unwrap_recursive_t<Uj>> is true, assigns GET<j>(rhs) to the value contained in *this. (Note: the left hand side is Ti, not VTi. This ensures that the existing storage is reused even for rvariant with duplicate corresponding alternatives; i.e., index() is unchanged.)

              •  — Otherwise, if either std::is_nothrow_constructible_v<VTi, Uj const&> is true or std::is_nothrow_move_constructible_v<VTi> is false, equivalent to emplace<VTi>(GET<j>(rhs)).

                @@ -1845,7 +1845,7 @@

                Assignmen

                 — rvariant_set::subset_of<rvariant<Us...>, rvariant> is true, and

              • -

                 — std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_type<Ts>>...> is false, and

                +

                 — std::disjunction_v<std::is_same<rvariant<Us...>, unwrap_recursive_t<Ts>>...> is false, and

              •  — std::is_constructible_v<VTi, Uj&&> && std::is_assignable_v<VTi&, Uj&&> is true for all j.

                @@ -1871,7 +1871,7 @@

                Assignmen

                 — Otherwise, if rhs holds a value but *this does not, initializes rvariant to hold VTi (with i being the index of the alternative corresponding to that of rhs) and direct-initializes the contained value with GET<j>(std::move(rhs)).

              • -

                 — Otherwise, if std::is_same_v<unwrap_recursive_type<Ti>, unwrap_recursive_type<Uj>> is true, assigns GET<j>(std::move(rhs)) to the value contained in *this. (Note: the left hand side is Ti, not VTi. This ensures that the existing storage is reused even for rvariant with duplicate corresponding alternatives; i.e., index() is unchanged.)

                +

                 — Otherwise, if std::is_same_v<unwrap_recursive_t<Ti>, unwrap_recursive_t<Uj>> is true, assigns GET<j>(std::move(rhs)) to the value contained in *this. (Note: the left hand side is Ti, not VTi. This ensures that the existing storage is reused even for rvariant with duplicate corresponding alternatives; i.e., index() is unchanged.)

              •  — Otherwise, equivalent to emplace<VTi>(GET<j>(std::move(rhs))).

                @@ -1894,7 +1894,7 @@

                Assignmen

                Modifiers [rvariant.mod]

                -
                template<class T, class... Args>
                +
                template<class T, class... Args>
                 constexpr T& emplace(Args&&... args);// 1
                 
                 template<class T, class U, class... Args>
                @@ -1914,23 +1914,23 @@ 

                Modifiers

                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>.

                +

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

                Effects: Equivalent to:
                  return emplace<I>(std::forward<Args>(args)...);
                -where I is the zero-based index of T in unwrap_recursive_type<Ts>.

                +where I is the zero-based index of T in unwrap_recursive_t<Ts>.

              • 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>.

                +

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

                Effects: Equivalent to:
                  return emplace<I>(il, std::forward<Args>(args)...);
                -where I is the zero-based index of T in unwrap_recursive_type<Ts>.

                +where I is the zero-based index of T in unwrap_recursive_t<Ts>.

              • @@ -1958,7 +1958,7 @@

                Modifiers Value status [rvariant.status]

                -
                constexpr bool valueless_by_exception() const noexcept;
                +
                constexpr bool valueless_by_exception() const noexcept;
                 constexpr std::size_t index() const noexcept;
                @@ -1970,7 +1970,7 @@

                Value sta

                Subset [rvariant.subset]

                -
                template<class... Us>
                +
                template<class... Us>
                   requires std::is_same_v<rvariant<Us...>, rvariant>
                 constexpr rvariant subset() const& noexcept(std::is_nothrow_copy_constructible_v<rvariant>);// 1
                 
                @@ -2061,7 +2061,7 @@ 

                Subset Swap [rvariant.swap]

                -
                constexpr void swap(rvariant&) noexcept(see below);
                +
                constexpr void swap(rvariant&) noexcept(see below);
                @@ -2075,7 +2075,7 @@

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

                rva Equivalent to the std::variant counterpart. [spec]

              • -

                2) The member typedef type denotes unwrap_recursive_type<TI>.

                +

                2) The member typedef type denotes unwrap_recursive_t<TI>.

                Mandates: I < sizeof...(Ts).

                @@ -2110,7 +2110,7 @@

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

                Flexibility t

                 — T is the same type as U or,

              • -

                 — unwrap_recursive_type<T> is the same type as U.

                +

                 — unwrap_recursive_t<T> is the same type as U.

              @@ -2157,7 +2157,7 @@

              Value access
              -
              namespace iris {
              +
              namespace iris {
               
               template<class T, class... Ts>
               constexpr bool holds_alternative(rvariant<Ts...> const& v) noexcept;
              @@ -2168,9 +2168,9 @@ 

              Value access
              • -

                Mandates: The type T occurs exactly once in unwrap_recursive_type<Ts>.

                +

                Mandates: The type T occurs exactly once in unwrap_recursive_t<Ts>.

                -

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

                +

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

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

                @@ -2180,7 +2180,7 @@

                Value access
                -
                template<std::size_t I, class... Ts>
                +
                template<std::size_t I, class... Ts>
                 constexpr see below& GET(rvariant<Ts...>& v);               // exposition only
                 
                 template<std::size_t I, class... Ts>
                @@ -2208,7 +2208,7 @@ 

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

                Value access
                -
                namespace iris {
                +
                namespace iris {
                 
                 template<class T, class... Ts> constexpr T&        get(rvariant<Ts...>& v);
                 template<class T, class... Ts> constexpr T&&       get(rvariant<Ts...>&& v);
                @@ -2254,9 +2254,9 @@ 

                Value access
                • -

                  Mandates: The type T occurs exactly once in unwrap_recursive_type<Ts>.

                  +

                  Mandates: The type T occurs exactly once in unwrap_recursive_t<Ts>.

                  -

                  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.

                  +

                  Effects: Let VT denote the type of the alternative held by v. If unwrap_recursive_t<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 or recursive_wrapper_alloca.

                  @@ -2266,7 +2266,7 @@

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

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

                  Value access
                  • -

                    1-2) Mandates: The type T occurs exactly once in unwrap_recursive_type<Ts>.

                    +

                    1-2) Mandates: The type T occurs exactly once in unwrap_recursive_t<Ts>.

                    -

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

                    +

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

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

                    @@ -2324,7 +2324,7 @@

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

                    Hash support
                    -
                    namespace std {
                    +
                    namespace std {
                     
                     template<class... Ts>
                     struct hash<::iris::rvariant<Ts...>>;// 1
                    @@ -2403,7 +2403,7 @@ 

                    Hash support

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

                    I/O operator<< support

                    -
                    // <iris/rvariant/rvariant_io.hpp>
                    +
                    // <iris/rvariant/rvariant_io.hpp>
                     
                     namespace iris {
                     
                    @@ -2478,7 +2478,7 @@ 

                    <

                  • -

                    2) Constraints: ADL-ostreamable<unwrap_recursive_type<Ti>> is true for all i.

                    +

                    2) Constraints: ADL-ostreamable<unwrap_recursive_t<Ti>> is true for all i.

                    Effects: Behaves as a formatted output function ([ostream.formatted.reqmts]) of os, except that:

                    @@ -2514,7 +2514,7 @@

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

                  • -

                    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:

                    +

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

                    • @@ -2530,7 +2530,7 @@

                      Example:

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

                    • @@ -2548,7 +2548,7 @@

                       — std::remove_cvref_t<Variant> is a specialization of rvariant, and

                    • -

                       — std::formattable<unwrap_recursive_type<Tsi>, charT> is true for all i, with Ts being the template parameter pack of cv-unqualified non-reference type for Variant.

                      +

                       — std::formattable<unwrap_recursive_t<Tsi>, charT> is true for all i, with Ts being the template parameter pack of cv-unqualified non-reference type for Variant.

                    @@ -2570,7 +2570,7 @@

                    Example:

                    -
                    using V = iris::rvariant<int, double>;
                    +
                    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
                    @@ -2584,7 +2584,7 @@

                    -
                    template<class... CharLike>
                    +
                    template<class... CharLike>
                     using select-char-t = see below; // exposition only
                    @@ -2597,7 +2597,7 @@

                    -
                    // <iris/rvariant/rvariant_io.hpp>
                    +
                    // <iris/rvariant/rvariant_io.hpp>
                     
                     template<class charT, class... Ts>
                     struct variant_format_string // exposition only
                    @@ -2641,7 +2641,7 @@ 

                    -
                    // <iris/rvariant/rvariant_io.hpp>
                    +
                    // <iris/rvariant/rvariant_io.hpp>
                     
                     template<class VFormat, class Variant>
                     struct variant_format_proxy // exposition only
                    @@ -2677,7 +2677,7 @@ 

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

                    Cla

                    -
                    namespace iris {
                    +
                    namespace iris {
                     
                     template<class T, class Allocator = std::allocator<T>>
                     class recursive_wrapper_alloca
                    @@ -2725,7 +2725,7 @@ 

                    Cla

                  @@ -2827,10 +2827,10 @@

                  -
                  namespace iris {
                  +
                  namespace iris {
                   
                   template<class T>
                  -using unwrap_recursive_type = see below;
                  +using unwrap_recursive_t = see below;
                   
                   } // iris
                  @@ -2838,13 +2838,16 @@

                  -
                  namespace iris {
                  +
                  namespace iris {
                   
                   template<class T>
                   constexpr auto&& unwrap_recursive(T&& o) noexcept;
                  @@ -2873,7 +2876,7 @@ 

                  -
                  using overload_type = R(*)(Visitor&&, Storage&&);
                  +
                  using overload_type = R(*)(Visitor&&, Storage&&);
                   constexpr overload_type vtable[] = {
                     &do_visit<0, Visitor, Storage>,
                     &do_visit<1, Visitor, Storage>,
                  @@ -2904,5 +2907,12 @@ 

                  License

                  + + \ No newline at end of file diff --git a/include/iris/config.hpp b/include/iris/config.hpp index 2ff9108..f64eb84 100644 --- a/include/iris/config.hpp +++ b/include/iris/config.hpp @@ -106,4 +106,10 @@ # endif #endif +// ------------------------------------------------------ + +#if defined(__RESHARPER__) && !defined(__cpp_lib_reference_from_temporary) +#define __cpp_lib_reference_from_temporary 202202L +#endif + #endif diff --git a/include/iris/rvariant/detail/variant_storage.hpp b/include/iris/rvariant/detail/variant_storage.hpp index 2baca3c..658ef66 100644 --- a/include/iris/rvariant/detail/variant_storage.hpp +++ b/include/iris/rvariant/detail/variant_storage.hpp @@ -10,11 +10,10 @@ #include #include -#include #include #include -#include +#include // IWYU pragma: keep #if defined(_MSC_VER) # define IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN \ @@ -298,15 +297,15 @@ template else if constexpr (I == 29) return std::forward(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.first; else if constexpr (I == 30) return std::forward(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.first; else if constexpr (I == 31) return std::forward(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.first; - else if constexpr (I < 64) return raw_get( + else if constexpr (I < 64) return detail::raw_get( std::forward(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest); - else return raw_get( + else return detail::raw_get( std::forward(storage).rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest .rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest.rest); } template -using raw_get_t = decltype(raw_get(std::declval())); +using raw_get_t = decltype(detail::raw_get(std::declval())); // -------------------------------------------------- // -------------------------------------------------- diff --git a/include/iris/rvariant/detail/visit.hpp b/include/iris/rvariant/detail/visit.hpp index f49d36a..3aaa36a 100644 --- a/include/iris/rvariant/detail/visit.hpp +++ b/include/iris/rvariant/detail/visit.hpp @@ -20,7 +20,7 @@ #include #include - +#include namespace iris { @@ -71,11 +71,10 @@ using raw_visit_result_t = decltype(std::declval()( template struct raw_visit_noexcept { - template struct lazy_invoke { - static constexpr std::size_t RealI = detail::valueless_unbias(I); - using type = std::is_nothrow_invocable< + static constexpr std::size_t RealI = detail::valueless_unbias(I); + using type = is_nothrow_directly_invocable< Visitor, std::in_place_index_t, raw_get_t @@ -84,12 +83,12 @@ struct raw_visit_noexcept static constexpr bool value = std::conditional_t< !std::remove_cvref_t::never_valueless && I == 0, - std::type_identity, - decltype(std::forward_like(std::declval())) + Storage >>, - lazy_invoke + lazy_invoke >::type::value; }; @@ -109,11 +108,11 @@ do_raw_visit(Visitor&& vis, Storage&& storage) // NOLINT(cppcoreguidelines-rval noexcept(raw_visit_noexcept::value) { if constexpr (!std::remove_cvref_t::never_valueless && I == 0) { - return std::invoke(std::forward(vis), std::in_place_index, std::forward(storage)); + return std::forward(vis)(std::in_place_index, std::forward(storage)); } else { constexpr std::size_t RealI = valueless_unbias(I); - return std::invoke(std::forward(vis), std::in_place_index, raw_get(std::forward(storage))); + return std::forward(vis)(std::in_place_index, raw_get(std::forward(storage))); } } @@ -171,7 +170,7 @@ struct raw_visit_dispatch { constexpr auto const& table = raw_visit_table::table; auto const& f = table[i]; - return std::invoke(f, std::forward(vis), std::forward(storage)); + return f(std::forward(vis), std::forward(storage)); } }; @@ -238,7 +237,9 @@ raw_visit(Variant&& v, Visitor&& vis) // NOLINT(cppcoreguidelines-missing-std-f noexcept(raw_visit_noexcept_all>) { constexpr std::size_t N = detail::valueless_bias(iris::variant_size_v>); - return raw_visit_dispatch::never_valueless, visit_strategy>::template apply( + return raw_visit_dispatch::never_valueless, visit_strategy>::template apply< + N, Visitor, forward_storage_t + >( detail::valueless_bias(v.index_), std::forward(vis), detail::forward_storage(v) @@ -251,7 +252,9 @@ raw_visit_i(std::size_t const biased_i, Variant&& v, Visitor&& vis) // NOLINT(c noexcept(raw_visit_noexcept_all>) { constexpr std::size_t N = detail::valueless_bias(iris::variant_size_v>); - return raw_visit_dispatch::never_valueless, visit_strategy>::template apply( + return raw_visit_dispatch::never_valueless, visit_strategy>::template apply< + N, Visitor, forward_storage_t + >( biased_i, std::forward(vis), detail::forward_storage(v) @@ -261,20 +264,11 @@ raw_visit_i(std::size_t const biased_i, Variant&& v, Visitor&& vis) // NOLINT(c // -------------------------------------------------- -// `std::invoke_result_t` MUST NOT be used here due to its side effects: -// -// In the case of `variant`, this is not a theoretical concern: -// it has observable consequences where `visit(...)` may be incorrectly -// instantiated during the invocation of `visit(...)`. -// This likely explains why [variant.visit](https://eel.is/c++draft/variant.visit#6) -// explicitly requires using `decltype(e(m))` instead of `std::invoke_result_t`. -// Also, we can't wrap this into a `struct` as it becomes not SFINAE-friendly. - template -using visit_result_t = decltype(std::invoke( // If you see an error here, your `T0` is not eligible for the `Visitor`. - std::declval(), - unwrap_recursive(detail::raw_get<0>(forward_storage(std::declval())))... -)); +using visit_result_t = std::invoke_result_t< // If you see an error here, your `T0` is not eligible for the `Visitor`. + Visitor, + decltype(unwrap_recursive(detail::raw_get<0>(detail::forward_storage(std::declval()))))... +>; template struct visit_check_impl; @@ -284,19 +278,13 @@ struct visit_check_impl> { static constexpr bool accepts_all_alternatives = std::is_invocable_v; - template - struct lazy_invoke - { - using type = decltype(std::invoke(std::declval(), std::declval()...)); - }; - // In case of `accepts_all_alternatives == false`, this // intentionally reports false-positive `true` to avoid // two `static_assert` errors. static constexpr bool same_return_type = std::is_same_v< typename std::conditional_t< accepts_all_alternatives, - lazy_invoke, + std::invoke_result, std::type_identity >::type, T0R @@ -308,16 +296,16 @@ struct visit_check_impl> template struct visit_check_impl, rvariant&, Rest...> - : std::conjunction&>, Rest...>...> {}; + : std::conjunction&>, Rest...>...> {}; template struct visit_check_impl, rvariant const&, Rest...> - : std::conjunction const&>, Rest...>...> {}; + : std::conjunction const&>, Rest...>...> {}; template struct visit_check_impl, rvariant&&, Rest...> - : std::conjunction>, Rest...>...> {}; + : std::conjunction>, Rest...>...> {}; template struct visit_check_impl, rvariant const&&, Rest...> - : std::conjunction const>, Rest...>...> {}; + : std::conjunction const>, Rest...>...> {}; template using visit_check = visit_check_impl, Variants...>; @@ -334,20 +322,13 @@ struct visit_R_check_impl> // mutually exclusive in order to provide better errors. static constexpr bool accepts_all_alternatives = std::is_invocable_v; - template - struct lazy_invoke - { - // This is NOT `std::invoke_r`; we need the plain type for conversion check - using type = decltype(std::invoke(std::declval(), std::declval()...)); - }; - // In case of `accepts_all_alternatives == false`, this // intentionally reports false-positive `true` to avoid // two `static_assert` errors. - static constexpr bool return_type_convertible_to_R = std::is_convertible_v< + static constexpr bool return_type_convertible_to_R = iris::detail::invoke_convertible< typename std::conditional_t< accepts_all_alternatives, - lazy_invoke, + std::invoke_result, std::type_identity >::type, R @@ -359,16 +340,16 @@ struct visit_R_check_impl> template struct visit_R_check_impl, rvariant&, Rest...> - : std::conjunction&>, Rest...>...> {}; + : std::conjunction&>, Rest...>...> {}; template struct visit_R_check_impl, rvariant const&, Rest...> - : std::conjunction const&>, Rest...>...> {}; + : std::conjunction const&>, Rest...>...> {}; template struct visit_R_check_impl, rvariant&&, Rest...> - : std::conjunction>, Rest...>...> {}; + : std::conjunction>, Rest...>...> {}; template struct visit_R_check_impl, rvariant const&&, Rest...> - : std::conjunction const>, Rest...>...> {}; + : std::conjunction const>, Rest...>...> {}; template using visit_R_check = visit_R_check_impl, Variants...>; @@ -383,14 +364,13 @@ template struct multi_visit_noexcept, Visitor, Storage...> { private: - template struct lazy_invoke { using type = std::is_nothrow_invocable_r< R, Visitor, - unwrap_recursive_type< - detail::raw_get_t(Is), Storage_> + unwrap_recursive_t< + detail::raw_get_t(Is), Storage> >... >; }; @@ -404,7 +384,7 @@ struct multi_visit_noexcept, Visitor, Storage...> >... >, std::type_identity>, // throw std::bad_variant_access{}; - lazy_invoke + lazy_invoke >::type::value; }; @@ -449,7 +429,8 @@ struct visit_table< Storage... > { - using function_type = R(*)(Visitor&&, Storage&&...); + using function_type = R(*)(Visitor&&, Storage&&...) + noexcept(multi_visit_noexcept, Visitor, Storage...>::value); static constexpr function_type table[] = { &multi_visitor::template apply... @@ -468,7 +449,7 @@ struct visit_dispatch<-1> { constexpr auto const& table = visit_table::table; auto const& f = table[flat_i]; - return std::invoke_r(f, std::forward(vis), std::forward(storage)...); + return f(std::forward(vis), std::forward(storage)...); } }; @@ -588,7 +569,9 @@ struct visit_impl< std::remove_cvref_t>::never_valueless... >::get(vars.index_...); - return visit_dispatch>::template apply( + return visit_dispatch>::template apply< + R, OverloadSeq, Visitor, forward_storage_t>... + >( flat_i, std::forward(vis), forward_storage>(vars)... ); } diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index 55811b0..1de4e30 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -41,7 +41,7 @@ template requires (!std::same_as) && (is_recursive_wrapper_like_v || is_recursive_wrapper_like_v) && - std::same_as, unwrap_recursive_type> + std::same_as, unwrap_recursive_t> struct check_recursive_wrapper_duplicate_impl : std::false_type { @@ -536,7 +536,9 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END -> detail::raw_visit_result_t(self.storage_))> { constexpr std::size_t N = detail::valueless_bias(sizeof...(Ts)); - return raw_visit_dispatch>::template apply( + return raw_visit_dispatch>::template apply< + N, Visitor, decltype(std::forward_like(self.storage_)) + >( detail::valueless_bias(self.index_), std::forward(vis), std::forward_like(self.storage_) @@ -591,7 +593,7 @@ class rvariant : private detail::rvariant_base_t static_assert((req::Cpp17Destructible && ...), "All types shall meet the Cpp17Destructible requirements ([variant.variant.general])."); static_assert(sizeof...(Ts) > 0, "A variant with no template arguments shall not be instantiated ([variant.variant.general])."); - using unwrapped_types = type_list...>; + using unwrapped_types = type_list...>; using base_type = detail::rvariant_base_t; friend struct detail::rvariant_base; @@ -684,18 +686,18 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END requires (!std::is_same_v, rvariant>) && rvariant_set::subset_of, rvariant> && - (!std::disjunction_v, unwrap_recursive_type>...>) && - std::conjunction_v, Ts...>, Us const&>...> + (!std::disjunction_v, unwrap_recursive_t>...>) && + std::conjunction_v, Ts...>, Us const&>...> constexpr rvariant(rvariant const& w) - noexcept(std::conjunction_v, Ts...>, Us const&>...>) + noexcept(std::conjunction_v, Ts...>, Us const&>...>) { w.raw_visit([this](std::in_place_index_t, [[maybe_unused]] Uj const& uj) - noexcept(std::conjunction_v, Ts...>, Us const&>...>) + noexcept(std::conjunction_v, Ts...>, Us const&>...>) { if constexpr (j != std::variant_npos) { - using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; + using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; using VT = maybe_wrapped::type; - static_assert(std::is_same_v, unwrap_recursive_type>); + static_assert(std::is_same_v, unwrap_recursive_t>); base_type::template construct_on_valueless(uj); } }); @@ -706,18 +708,18 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END requires (!std::is_same_v, rvariant>) && rvariant_set::subset_of, rvariant> && - (!std::disjunction_v, unwrap_recursive_type>...>) && - std::conjunction_v, Ts...>, Us&&>...> + (!std::disjunction_v, unwrap_recursive_t>...>) && + std::conjunction_v, Ts...>, Us&&>...> constexpr rvariant(rvariant&& w) - noexcept(std::conjunction_v, Ts...>, Us&&>...>) + noexcept(std::conjunction_v, Ts...>, Us&&>...>) { std::move(w).raw_visit([this](std::in_place_index_t, [[maybe_unused]] Uj&& uj) - noexcept(std::conjunction_v, Ts...>, Us&&>...>) + noexcept(std::conjunction_v, Ts...>, Us&&>...>) { if constexpr (j != std::variant_npos) { - using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; + using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; using VT = maybe_wrapped::type; - static_assert(std::is_same_v, unwrap_recursive_type>); + static_assert(std::is_same_v, unwrap_recursive_t>); static_assert(std::is_rvalue_reference_v); base_type::template construct_on_valueless(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) } @@ -731,30 +733,30 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END requires (!std::is_same_v, rvariant>) && rvariant_set::subset_of, rvariant> && - (!std::disjunction_v, unwrap_recursive_type>...>) && - std::conjunction_v, Ts...>, Us const&>...> + (!std::disjunction_v, unwrap_recursive_t>...>) && + std::conjunction_v, Ts...>, Us const&>...> constexpr rvariant& operator=(rvariant const& rhs) - noexcept(std::conjunction_v, Ts...>, Us const&>...>) + noexcept(std::conjunction_v, Ts...>, Us const&>...>) { rhs.raw_visit([this](std::in_place_index_t, [[maybe_unused]] Uj const& uj) - noexcept(std::conjunction_v, Ts...>, Us const&>...>) + noexcept(std::conjunction_v, Ts...>, Us const&>...>) { if constexpr (j == std::variant_npos) { this->visit_reset(); } else { - using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; + using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; using VT = maybe_wrapped::type; - static_assert(std::is_same_v, unwrap_recursive_type>); + static_assert(std::is_same_v, unwrap_recursive_t>); this->raw_visit([this, &uj](std::in_place_index_t, [[maybe_unused]] Ti& ti) - noexcept(std::conjunction_v, Ts...>, Us const&>...>) + noexcept(std::conjunction_v, Ts...>, Us const&>...>) { 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(uj); - } else if constexpr (std::is_same_v, unwrap_recursive_type>) { + } else if constexpr (std::is_same_v, unwrap_recursive_t>) { ti = uj; } else if constexpr (std::is_nothrow_constructible_v || !std::is_nothrow_move_constructible_v) { @@ -775,31 +777,31 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END requires (!std::is_same_v, rvariant>) && rvariant_set::subset_of, rvariant> && - (!std::disjunction_v, unwrap_recursive_type>...>) && - std::conjunction_v, Ts...>, Us&&>...> + (!std::disjunction_v, unwrap_recursive_t>...>) && + std::conjunction_v, Ts...>, Us&&>...> constexpr rvariant& operator=(rvariant&& rhs) - noexcept(std::conjunction_v, Ts...>, Us&&>...>) + noexcept(std::conjunction_v, Ts...>, Us&&>...>) { std::move(rhs).raw_visit([this](std::in_place_index_t, [[maybe_unused]] Uj&& uj) - noexcept(std::conjunction_v, Ts...>, Us&&>...>) + noexcept(std::conjunction_v, Ts...>, Us&&>...>) { if constexpr (j == std::variant_npos) { this->visit_reset(); } else { - using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; + using maybe_wrapped = detail::select_maybe_wrapped, Ts...>; using VT = maybe_wrapped::type; - static_assert(std::is_same_v, unwrap_recursive_type>); + static_assert(std::is_same_v, unwrap_recursive_t>); this->raw_visit([this, &uj](std::in_place_index_t, [[maybe_unused]] Ti& ti) - noexcept(std::conjunction_v, Ts...>, Us&&>...>) + noexcept(std::conjunction_v, Ts...>, Us&&>...>) { 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(std::move(uj)); // NOLINT(bugprone-move-forwarding-reference) - } else if constexpr (std::is_same_v, unwrap_recursive_type>) { + } else if constexpr (std::is_same_v, unwrap_recursive_t>) { ti = std::move(uj); // NOLINT(bugprone-move-forwarding-reference) } else { @@ -1106,9 +1108,6 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END template friend struct detail::forward_storage_t_impl; - template - friend struct detail::forward_storage_t_impl; - template friend constexpr detail::forward_storage_t&& detail::forward_storage(std::remove_reference_t& v IRIS_LIFETIMEBOUND) noexcept; @@ -1140,38 +1139,38 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_END template requires std::conjunction_v, Ts_>...> friend constexpr bool operator==(rvariant const&, rvariant const&) - noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); + noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); template requires std::conjunction_v, Ts_>...> friend constexpr bool operator!=(rvariant const&, rvariant const&) - noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); + noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); template requires std::conjunction_v, Ts_>...> friend constexpr bool operator<(rvariant const&, rvariant const&) - noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); + noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); template requires std::conjunction_v, Ts_>...> friend constexpr bool operator>(rvariant const&, rvariant const&) - noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); + noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); template requires std::conjunction_v, Ts_>...> friend constexpr bool operator<=(rvariant const&, rvariant const&) - noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); + noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); template requires std::conjunction_v, Ts_>...> friend constexpr bool operator>=(rvariant const&, rvariant const&) - noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); + noexcept(std::conjunction_v, Ts_ const&, Ts_ const&>...>); template requires (std::three_way_comparable && ...) friend constexpr std::common_comparison_category_t...> operator<=>(rvariant const&, rvariant const&) - noexcept(std::conjunction_v...>, std::compare_three_way, Ts_ const&, Ts_ const& >...>); @@ -1444,7 +1443,7 @@ struct relops_visitor [[nodiscard]] IRIS_FORCEINLINE constexpr R operator()(std::in_place_index_t, T const& w_alt) const noexcept(std::disjunction_v< std::bool_constant, - std::is_nothrow_invocable_r + is_nothrow_directly_invocable_r >) { if constexpr (i != std::variant_npos) { @@ -1462,7 +1461,7 @@ struct relops_visitor template requires std::conjunction_v, Ts>...> [[nodiscard]] constexpr bool operator==(rvariant const& v, rvariant const& w) - noexcept(std::conjunction_v, Ts const&, Ts const&>...>) + noexcept(std::conjunction_v, Ts const&, Ts const&>...>) { auto const vi = detail::valueless_bias>(v.index_); auto const wi = detail::valueless_bias>(w.index_); @@ -1472,7 +1471,7 @@ template template requires std::conjunction_v, Ts>...> [[nodiscard]] constexpr bool operator!=(rvariant const& v, rvariant const& w) - noexcept(std::conjunction_v, Ts const&, Ts const&>...>) + noexcept(std::conjunction_v, Ts const&, Ts const&>...>) { auto const vi = detail::valueless_bias>(v.index_); auto const wi = detail::valueless_bias>(w.index_); @@ -1482,7 +1481,7 @@ template template requires std::conjunction_v, Ts>...> [[nodiscard]] constexpr bool operator<(rvariant const& v, rvariant const& w) - noexcept(std::conjunction_v, Ts const&, Ts const&>...>) + noexcept(std::conjunction_v, Ts const&, Ts const&>...>) { auto const vi = detail::valueless_bias>(v.index_); auto const wi = detail::valueless_bias>(w.index_); @@ -1522,7 +1521,7 @@ template template requires std::conjunction_v, Ts>...> [[nodiscard]] constexpr bool operator>(rvariant const& v, rvariant const& w) - noexcept(std::conjunction_v, Ts const&, Ts const&>...>) + noexcept(std::conjunction_v, Ts const&, Ts const&>...>) { auto const vi = detail::valueless_bias>(v.index_); auto const wi = detail::valueless_bias>(w.index_); @@ -1533,7 +1532,7 @@ template template requires std::conjunction_v, Ts>...> [[nodiscard]] constexpr bool operator<=(rvariant const& v, rvariant const& w) - noexcept(std::conjunction_v, Ts const&, Ts const&>...>) + noexcept(std::conjunction_v, Ts const&, Ts const&>...>) { auto const vi = detail::valueless_bias>(v.index_); auto const wi = detail::valueless_bias>(w.index_); @@ -1544,7 +1543,7 @@ template template requires std::conjunction_v, Ts>...> [[nodiscard]] constexpr bool operator>=(rvariant const& v, rvariant const& w) - noexcept(std::conjunction_v, Ts const&, Ts const&>...>) + noexcept(std::conjunction_v, Ts const&, Ts const&>...>) { auto const vi = detail::valueless_bias>(v.index_); auto const wi = detail::valueless_bias>(w.index_); @@ -1557,7 +1556,7 @@ template requires (std::three_way_comparable && ...) [[nodiscard]] IRIS_FORCEINLINE constexpr std::common_comparison_category_t...> operator<=>(rvariant const& v, rvariant const& w) - noexcept(std::conjunction_v...>, std::compare_three_way, Ts const&, Ts const& >...>) diff --git a/include/iris/rvariant/rvariant_io.hpp b/include/iris/rvariant/rvariant_io.hpp index ac5d769..5cbf7e7 100644 --- a/include/iris/rvariant/rvariant_io.hpp +++ b/include/iris/rvariant/rvariant_io.hpp @@ -48,7 +48,7 @@ template // Required to work around MSVC bug where it instantiates this function // for completely irrelevant call, e.g. `std::cout << "foo"sv << 'c' << std::endl;` (sizeof...(Ts) > 0) && - std::conjunction_v>...> + std::conjunction_v>...> std::ostream& operator<<(std::ostream& os, rvariant const& v) { std::ostream::sentry sentry(os); @@ -152,7 +152,7 @@ variant_format(Fmts&&... fmts) noexcept } template> -[[nodiscard]] IRIS_CONSTEXPR_UP typename detail::variant_format_for_impl>::spec_type +[[nodiscard]] IRIS_CONSTEXPR_UP detail::variant_format_for_impl>::spec_type variant_format_for(Fmts&&... fmts) noexcept { static_assert(is_ttp_specialization_of_v, rvariant>); @@ -183,7 +183,7 @@ struct variant_alts_formattable : std::false_type template struct variant_alts_formattable> - : std::bool_constant<(std::formattable, charT> && ...)> + : std::bool_constant<(std::formattable, charT> && ...)> {}; } // detail @@ -194,10 +194,10 @@ struct variant_alts_formattable> namespace std { template - requires (std::formattable<::iris::unwrap_recursive_type, charT> && ...) + requires (std::formattable<::iris::unwrap_recursive_t, charT> && ...) struct formatter<::iris::rvariant, charT> // NOLINT(cert-dcl58-cpp) { - static constexpr typename std::basic_format_parse_context::const_iterator + static constexpr std::basic_format_parse_context::const_iterator parse(std::basic_format_parse_context& ctx) { if (ctx.begin() == ctx.end()) return ctx.begin(); @@ -221,7 +221,7 @@ struct formatter<::iris::rvariant, charT> // NOLINT(cert-dcl58-cpp) } else { return std::format_to( ctx.out(), - ::iris::format_traits::template brace_full<::iris::unwrap_recursive_type const&>, + ::iris::format_traits::template brace_full<::iris::unwrap_recursive_t const&>, ::iris::unwrap_recursive(alt) ); } @@ -237,7 +237,7 @@ template ::iris::detail::variant_alts_formattable>::value struct formatter<::iris::detail::variant_format_proxy, charT> // NOLINT(cert-dcl58-cpp) { - static constexpr typename std::basic_format_parse_context::const_iterator + static constexpr std::basic_format_parse_context::const_iterator parse(std::basic_format_parse_context& ctx) { if (ctx.begin() == ctx.end()) return ctx.begin(); @@ -259,12 +259,12 @@ struct formatter<::iris::detail::variant_format_proxy, charT> ::iris::detail::throw_bad_variant_access(); } else { static_assert( - std::is_invocable_v>>, + std::is_invocable_v>>, "`VFormat` must provide format string for all alternative types." ); return std::format_to( ctx.out(), - std::invoke(proxy.v_fmt, std::in_place_type<::iris::unwrap_recursive_type>), + std::invoke(proxy.v_fmt, std::in_place_type<::iris::unwrap_recursive_t>), ::iris::unwrap_recursive(alt) ); } diff --git a/include/iris/rvariant/subset.hpp b/include/iris/rvariant/subset.hpp index ab4fbff..758e4f5 100644 --- a/include/iris/rvariant/subset.hpp +++ b/include/iris/rvariant/subset.hpp @@ -49,7 +49,7 @@ struct is_subset_of, rvariant> : std::conjunction< std::disjunction< is_in, - is_in...> + is_in...> >... > {}; diff --git a/include/iris/rvariant/variant_helper.hpp b/include/iris/rvariant/variant_helper.hpp index 0b3f40c..4baa6e6 100644 --- a/include/iris/rvariant/variant_helper.hpp +++ b/include/iris/rvariant/variant_helper.hpp @@ -77,23 +77,41 @@ constexpr bool is_recursive_wrapper_like_v = is_ttp_specialization_of_v; template -struct unwrap_recursive_type_impl +struct unwrap_recursive_impl { using type = T; }; template -struct unwrap_recursive_type_impl> +struct unwrap_recursive_impl> { using type = T; }; template -struct unwrap_recursive_type_impl> +struct unwrap_recursive_impl> { using type = T; }; +template +struct unwrap_recursive_impl +{ + using type = unwrap_recursive_impl::type const; +}; + +template +struct unwrap_recursive_impl +{ + using type = unwrap_recursive_impl::type&; +}; + +template +struct unwrap_recursive_impl +{ + using type = unwrap_recursive_impl::type&&; +}; + struct unwrap_recursive_fn { template @@ -108,10 +126,10 @@ struct unwrap_recursive_fn } }; -} // detail +} // detail template -using unwrap_recursive_type = detail::unwrap_recursive_type_impl::type; +using unwrap_recursive_t = detail::unwrap_recursive_impl::type; inline constexpr detail::unwrap_recursive_fn unwrap_recursive{}; @@ -126,7 +144,7 @@ template struct variant_alternative : std::add_const> {}; template -struct variant_alternative> : pack_indexing...> +struct variant_alternative> : pack_indexing...> { static_assert(I < sizeof...(Ts)); }; diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index 7549206..2210a75 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -364,22 +364,11 @@ struct is_trivially_swappable : std::conjunction< template<> struct is_trivially_swappable : std::true_type {}; template -constexpr bool is_trivially_swappable_v = is_trivially_swappable::value; +inline constexpr bool is_trivially_swappable_v = is_trivially_swappable::value; namespace detail { -// Workaround for MSVC 2026's Intellisense -template -inline constexpr bool reference_converts_from_temporary_workaround_v = -#if defined(__cpp_lib_reference_from_temporary) - std::reference_converts_from_temporary_v; -#elif defined(__has_builtin) && __has_builtin(__reference_converts_from_temporary) - __reference_converts_from_temporary(To, From); -#else - true; -#endif - // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p0870r8.html template @@ -424,7 +413,7 @@ template requires std::is_reference_v && (!is_never_narrowing_family::value) && - (!reference_converts_from_temporary_workaround_v) + (!std::reference_converts_from_temporary_v) struct is_convertible_without_narrowing_dispatch : std::true_type {}; @@ -433,7 +422,7 @@ template requires std::is_reference_v && (!is_never_narrowing_family::value) && - reference_converts_from_temporary_workaround_v + std::reference_converts_from_temporary_v struct is_convertible_without_narrowing_dispatch : is_convertible_without_narrowing_array_check< From, @@ -537,6 +526,103 @@ struct no_narrowing_resolution< template struct no_narrowing_resolution : detail::no_narrowing_resolution {}; +// ---------------------------------------------- + +template +// ReSharper disable once CppFunctionDoesntReturnValue +[[nodiscard]] T declval_exact() noexcept +{ + // ReSharper disable once CppStaticAssertFailure + static_assert(false, "declval_exact() must not be odr-used"); +} + +template +// ReSharper disable once CppFunctionDoesntReturnValue +[[nodiscard]] T copy_initialize(T) noexcept +{ + // ReSharper disable once CppStaticAssertFailure + static_assert(false, "copy_initialize() must not be odr-used"); +} + +// ---------------------------------------------- + +namespace detail { + +// The conversion part of INVOKE: `To` is cv void, or VAL can be +// implicitly converted to `To` without binding a reference to a temporary. +template +concept invoke_convertible = + std::is_void_v || + requires { + iris::copy_initialize(iris::declval_exact()); + requires !std::reference_converts_from_temporary_v; + }; + +} // detail + +template +concept directly_invocable = requires(F&& f, Args&&... args) { + typename std::void_t(f)(static_cast(args)...))>; +}; + +template +struct directly_invoke_result {}; + +template + requires directly_invocable +struct directly_invoke_result +{ + using type = decltype(std::declval()(std::declval()...)); +}; +template +using directly_invoke_result_t = directly_invoke_result::type; + +template +concept directly_invocable_r = + directly_invocable && + detail::invoke_convertible()(std::declval()...)), R>; + +template +struct is_directly_invocable : std::bool_constant> {}; +template +inline constexpr bool is_directly_invocable_v = is_directly_invocable::value; + +template +struct is_directly_invocable_r : std::bool_constant> {}; +template +inline constexpr bool is_directly_invocable_r_v = is_directly_invocable_r::value; + +template +struct is_nothrow_directly_invocable + : std::bool_constant(f)(static_cast(args)...) } noexcept; + }> +{}; +template +inline constexpr bool is_nothrow_directly_invocable_v = is_nothrow_directly_invocable::value; + +template +struct is_nothrow_directly_invocable_r : std::false_type {}; + +template + requires std::is_void_v +struct is_nothrow_directly_invocable_r + : is_nothrow_directly_invocable +{}; + +template + requires + (!std::is_void_v) && directly_invocable_r && + requires(F&& f, Args&&... args) { + { iris::copy_initialize(static_cast(f)(static_cast(args)...)) } noexcept; + } +struct is_nothrow_directly_invocable_r + : std::true_type +{}; + +template +inline constexpr bool is_nothrow_directly_invocable_r_v = is_nothrow_directly_invocable_r::value; + } // iris #endif diff --git a/test/rvariant/get_visit.cpp b/test/rvariant/get_visit.cpp index 9daf4ba..08871e5 100644 --- a/test/rvariant/get_visit.cpp +++ b/test/rvariant/get_visit.cpp @@ -796,4 +796,132 @@ TEST_CASE("visit", "[wrapper]") } } +struct Node +{ + int value; +}; +using Wrapped = iris::recursive_wrapper; +using RecursiveVariant = iris::rvariant; +using MaybeValuelessVariant = iris::rvariant; + +struct NothrowVisitor +{ + int operator()(Node const&) const noexcept { return 0; } + int operator()(int const&) const noexcept { return 1; } +}; + +struct ThrowingVisitor +{ + int operator()(Node const&) const { return 0; } + int operator()(int const&) const noexcept { return 1; } +}; + +struct UnwrapSensitiveVisitor +{ + template + int operator()(T const&) const noexcept(std::is_same_v || std::is_same_v) { return 0; } +}; + +struct BinaryNothrowVisitor +{ + template + int operator()(T const&, U const&) const noexcept { return 0; } +}; + +struct NonMovable +{ + explicit NonMovable(int value) noexcept : value(value) {} + NonMovable(NonMovable const&) = delete; + NonMovable(NonMovable&&) = delete; + + int value; +}; + +struct NonMovableVisitor +{ + template + NonMovable operator()(T const&) const noexcept { return NonMovable{42}; } +}; + +struct ByValueVisitor +{ + template + int operator()(T const&) const noexcept { return 42; } +}; + +struct ByReferenceVisitor +{ + template + int const& operator()(T const&) const noexcept + { + static constexpr int value = 42; + return value; + } +}; + +struct ThrowingEq +{ + friend bool operator==(ThrowingEq const&, ThrowingEq const&) { return true; } + friend std::strong_ordering operator<=>(ThrowingEq const&, ThrowingEq const&) { return std::strong_ordering::equal; } +}; + +TEST_CASE("visit (noexcept)") +{ + STATIC_CHECK(noexcept(iris::visit(NothrowVisitor{}, std::declval()))); + STATIC_CHECK(noexcept(iris::visit(NothrowVisitor{}, std::declval()))); + STATIC_CHECK(noexcept(iris::visit(NothrowVisitor{}, std::declval()))); + STATIC_CHECK(noexcept(iris::visit(NothrowVisitor{}, std::declval()))); + STATIC_CHECK(noexcept(iris::visit(UnwrapSensitiveVisitor{}, std::declval()))); + STATIC_CHECK(noexcept(iris::visit(UnwrapSensitiveVisitor{}, std::declval()))); + STATIC_CHECK(!noexcept(iris::visit(ThrowingVisitor{}, std::declval()))); + + STATIC_CHECK(noexcept(iris::visit(NothrowVisitor{}, std::declval()))); + STATIC_CHECK(noexcept(iris::visit(NothrowVisitor{}, std::declval()))); + STATIC_CHECK(!noexcept(iris::visit(ThrowingVisitor{}, std::declval()))); + + STATIC_CHECK(noexcept(std::declval().visit(NothrowVisitor{}))); + STATIC_CHECK(!noexcept(std::declval().visit(ThrowingVisitor{}))); + + STATIC_CHECK(noexcept(iris::visit(BinaryNothrowVisitor{}, std::declval(), std::declval()))); + + STATIC_CHECK(!noexcept(iris::visit(ByValueVisitor{}, std::declval()))); +} + +TEST_CASE("visit: return type conversion") +{ + RecursiveVariant const v{Node{1}}; + + STATIC_CHECK(std::is_void_v(ByValueVisitor{}, v))>); + STATIC_CHECK(std::is_void_v(ByValueVisitor{}, v))>); + iris::visit(ByValueVisitor{}, v); + v.visit(ByValueVisitor{}); + + NonMovable const by_visit = iris::visit(NonMovableVisitor{}, v); + CHECK(by_visit.value == 42); + NonMovable const by_visit_r = iris::visit(NonMovableVisitor{}, v); + CHECK(by_visit_r.value == 42); + + CHECK(iris::visit(ByValueVisitor{}, v) == 42L); + CHECK(iris::visit(ByReferenceVisitor{}, v) == 42); +} + +TEST_CASE("relops noexcept") +{ + using Nothrow = iris::rvariant; + using Throwing = iris::rvariant; + + STATIC_CHECK(noexcept(std::declval() == std::declval())); + STATIC_CHECK(noexcept(std::declval() != std::declval())); + STATIC_CHECK(noexcept(std::declval() < std::declval())); + STATIC_CHECK(noexcept(std::declval() <=> std::declval())); + + STATIC_CHECK(!noexcept(std::declval() == std::declval())); + STATIC_CHECK(!noexcept(std::declval() <=> std::declval())); + + CHECK(Nothrow{1} == Nothrow{1}); + CHECK(Nothrow{1} != Nothrow{2.0}); + CHECK(Nothrow{1} < Nothrow{2}); + CHECK(((Nothrow{1} <=> Nothrow{1}) == 0)); +} + } // unit_test diff --git a/test/rvariant/rvariant.cpp b/test/rvariant/rvariant.cpp index 952ed0b..3803f82 100644 --- a/test/rvariant/rvariant.cpp +++ b/test/rvariant/rvariant.cpp @@ -1616,6 +1616,35 @@ TEST_CASE("unwrap_recursive") // not [recursive] STATIC_REQUIRE(std::is_same_v&&>())), int&&>); STATIC_REQUIRE(std::is_same_v const&>())), int const&>); STATIC_REQUIRE(std::is_same_v const&&>())), int const&&>); + + struct Node + { + int value; + }; + using Wrapped = iris::recursive_wrapper; + + STATIC_CHECK(std::is_same_v, Node>); + STATIC_CHECK(std::is_same_v, int>); + STATIC_CHECK(std::is_same_v&, Node&>); + STATIC_CHECK(std::is_same_v const&, Node const&>); + STATIC_CHECK(std::is_same_v const, Node const>); + + STATIC_CHECK(std::is_same_v, Node const>); + STATIC_CHECK(std::is_same_v, Node&>); + STATIC_CHECK(std::is_same_v, Node const&>); + STATIC_CHECK(std::is_same_v, Node&&>); + STATIC_CHECK(std::is_same_v, Node const&&>); + STATIC_CHECK(std::is_same_v, int const&>); + STATIC_CHECK(std::is_same_v&>, Node const&>); + + STATIC_CHECK(std::is_same_v, decltype(iris::unwrap_recursive(std::declval()))>); + STATIC_CHECK(std::is_same_v, decltype(iris::unwrap_recursive(std::declval()))>); + STATIC_CHECK(std::is_same_v, decltype(iris::unwrap_recursive(std::declval()))>); + STATIC_CHECK(std::is_same_v, decltype(iris::unwrap_recursive(std::declval()))>); + STATIC_CHECK(std::is_same_v, decltype(iris::unwrap_recursive(std::declval()))>); + STATIC_CHECK(std::is_same_v, decltype(iris::unwrap_recursive(std::declval()))>); + + STATIC_CHECK(std::is_same_v&&, decltype(iris::unwrap_recursive(std::declval()))>); } TEST_CASE("maybe_wrapped") // not [recursive] diff --git a/test/type_traits.cpp b/test/type_traits.cpp index f941dea..53c52c1 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -448,28 +448,24 @@ TEST_CASE("is_convertible_without_narrowing: class types with conversion") operator double(); }; STATIC_CHECK(!iris::is_convertible_without_narrowing::value); + // ReSharper disable once CppStaticAssertFailure STATIC_CHECK(!iris::is_convertible_without_narrowing::value); } TEST_CASE("is_convertible_without_narrowing: class types with conversion operator") { - // Implicit conversion operator STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - // Explicit conversion operator: is_convertible is false STATIC_CHECK(!iris::is_convertible_without_narrowing_v); } TEST_CASE("is_convertible_without_narrowing: class types inheritance") { - // Derived to base (by value): implicitly convertible STATIC_CHECK(iris::is_convertible_without_narrowing_v); - // Base to derived: not implicitly convertible STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - // Derived& to base: implicitly convertible STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); } @@ -593,7 +589,7 @@ TEST_CASE("is_convertible_without_narrowing: function pointers") // Function pointer to bool: narrowing STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - // Function pointer to void*: not allowe per standard, but MSVC accepts this conversion + // Function pointer to void*: not allowed as per standard, but MSVC accepts this conversion // STATIC_CHECK(!iris::is_convertible_without_narrowing_v); } @@ -616,7 +612,6 @@ TEST_CASE("is_convertible_without_narrowing: array and function types") STATIC_CHECK(iris::is_convertible_without_narrowing_v); } - TEST_CASE("specialization_of") { STATIC_CHECK(iris::is_ttp_specialization_of_v, tuple>); @@ -635,3 +630,293 @@ TEST_CASE("specialization_of") STATIC_CHECK(!iris::is_ctp_specialization_of_v, n_list>); STATIC_CHECK(!iris::is_ctp_specialization_of_v, n_list>); } + + +struct Incomplete; + +struct NonMovable +{ + NonMovable(NonMovable const&) = delete; + NonMovable(NonMovable&&) = delete; +}; + +struct ThrowingMove +{ + ThrowingMove(ThrowingMove&&) noexcept(false); +}; + +struct ThrowingConv +{ + ThrowingConv(int); +}; + +struct NothrowConv +{ + NothrowConv(int) noexcept; +}; + +struct ExplicitConv +{ + explicit ExplicitConv(int); +}; + +struct ThrowingDtor +{ + ~ThrowingDtor() noexcept(false); +}; + +struct NoDtor +{ + ~NoDtor() = delete; +}; + +// Hard error when instantiated +template +struct Poison +{ + static_assert(sizeof(T) == 0, "Poison must not be instantiated"); + T value; +}; + +struct WithMembers +{ + [[maybe_unused]] int data; + [[maybe_unused]] int function() const; +}; + +struct NonConstCallable +{ + void operator()(); +}; + +struct LvalueOnlyCallable +{ + void operator()() &; +}; + +struct RvalueOnlyCallable +{ + void operator()() &&; +}; + +struct GenericCallable +{ + template + T operator()(T const&) const noexcept; +}; + +template +concept copy_initializable_from_exact = requires { + iris::copy_initialize(iris::declval_exact()); +}; + +template +concept has_directly_invoke_result = requires { + typename iris::directly_invoke_result::type; +}; + +template +using declval_exact_t = decltype(iris::declval_exact()); + +TEST_CASE("declval_exact", "[type_traits]") +{ + STATIC_CHECK(std::same_as()), int&>); + STATIC_CHECK(std::same_as()), int const&>); + STATIC_CHECK(std::same_as()), int&&>); + STATIC_CHECK(std::same_as()), void>); + + STATIC_CHECK(std::same_as()), int>); + STATIC_CHECK(std::same_as()), NonMovable>); + STATIC_CHECK(std::same_as, Incomplete>); + STATIC_CHECK(std::same_as()), int&&>); + + STATIC_CHECK(noexcept(iris::declval_exact())); + STATIC_CHECK(noexcept(iris::copy_initialize(0))); + + STATIC_CHECK(copy_initializable_from_exact); + STATIC_CHECK(!copy_initializable_from_exact); + STATIC_CHECK(!std::is_convertible_v); + + STATIC_CHECK(copy_initializable_from_exact); + STATIC_CHECK(!copy_initializable_from_exact); + STATIC_CHECK(!copy_initializable_from_exact); + STATIC_CHECK(!copy_initializable_from_exact); + STATIC_CHECK(!copy_initializable_from_exact); + + STATIC_CHECK(noexcept(iris::copy_initialize(iris::declval_exact()))); + STATIC_CHECK(!noexcept(iris::copy_initialize(iris::declval_exact()))); + STATIC_CHECK(noexcept(iris::copy_initialize(iris::declval_exact()))); + // ReSharper disable once CppStaticAssertFailure + STATIC_CHECK(!noexcept(iris::copy_initialize(iris::declval_exact()))); + // ReSharper disable once CppStaticAssertFailure + STATIC_CHECK(!noexcept(iris::copy_initialize(iris::declval_exact()))); +} + +TEST_CASE("invoke_convertible", "[type_traits]") +{ + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(!iris::detail::invoke_convertible); + + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(iris::detail::invoke_convertible, void>); + STATIC_CHECK(!iris::detail::invoke_convertible); + + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(!iris::detail::invoke_convertible); + // ReSharper disable once CppStaticAssertFailure + STATIC_CHECK(!iris::detail::invoke_convertible); + + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(!iris::detail::invoke_convertible); + STATIC_CHECK(!iris::detail::invoke_convertible); + STATIC_CHECK(iris::detail::invoke_convertible); + + STATIC_CHECK(!iris::detail::invoke_convertible); + STATIC_CHECK(!iris::detail::invoke_convertible); + STATIC_CHECK(!iris::detail::invoke_convertible); + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(iris::detail::invoke_convertible); + STATIC_CHECK(!iris::detail::invoke_convertible); +} + +TEST_CASE("directly_invocable", "[type_traits]") +{ + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + // ReSharper disable once CppStaticAssertFailure + STATIC_CHECK(!iris::directly_invocable); + + STATIC_CHECK(std::invocable); + STATIC_CHECK(!iris::directly_invocable); + STATIC_CHECK(std::invocable); + STATIC_CHECK(!iris::directly_invocable); + + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable); + STATIC_CHECK(iris::directly_invocable(*)()>); + + STATIC_CHECK(iris::is_directly_invocable_v); + STATIC_CHECK(!iris::is_directly_invocable_v); + STATIC_CHECK(iris::is_directly_invocable::value); + + STATIC_CHECK(std::same_as, int>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int&&>); + STATIC_CHECK(std::same_as, void>); + STATIC_CHECK(std::same_as, long>); + STATIC_CHECK(std::same_as, Incomplete>); + STATIC_CHECK(std::same_as(*)()>, Poison>); + + // SFINAE-friendly + STATIC_CHECK(has_directly_invoke_result); + STATIC_CHECK(!has_directly_invoke_result); + STATIC_CHECK(!has_directly_invoke_result); + STATIC_CHECK(!has_directly_invoke_result); +} + +TEST_CASE("directly_invocable_r", "[type_traits]") +{ + STATIC_CHECK(iris::directly_invocable_r); + STATIC_CHECK(iris::directly_invocable_r); + STATIC_CHECK(iris::directly_invocable_r); + STATIC_CHECK(!iris::directly_invocable_r); + STATIC_CHECK(!iris::directly_invocable_r); + STATIC_CHECK(!iris::directly_invocable_r); + + STATIC_CHECK(iris::directly_invocable_r); + STATIC_CHECK(!iris::directly_invocable_r); + + STATIC_CHECK(iris::directly_invocable_r); + STATIC_CHECK(iris::directly_invocable_r); + STATIC_CHECK(!iris::directly_invocable_r); + STATIC_CHECK(!iris::directly_invocable_r); + + STATIC_CHECK(!iris::directly_invocable_r); + STATIC_CHECK(!iris::directly_invocable_r); + STATIC_CHECK(!iris::directly_invocable_r); + STATIC_CHECK(iris::directly_invocable_r); + STATIC_CHECK(iris::directly_invocable_r); + + STATIC_CHECK(iris::directly_invocable_r); + STATIC_CHECK(iris::directly_invocable_r); + STATIC_CHECK(iris::directly_invocable_r(*)()>); + STATIC_CHECK(!iris::directly_invocable_r); + // ReSharper disable once CppStaticAssertFailure + STATIC_CHECK(!iris::directly_invocable_r); + + STATIC_CHECK(iris::is_directly_invocable_r_v); + STATIC_CHECK(!iris::is_directly_invocable_r_v); + STATIC_CHECK(iris::is_directly_invocable_r::value); +} + +TEST_CASE("is_nothrow_directly_invocable", "[type_traits]") +{ + STATIC_CHECK(iris::is_nothrow_directly_invocable_v); + STATIC_CHECK(!iris::is_nothrow_directly_invocable_v); + STATIC_CHECK(!iris::is_nothrow_directly_invocable_v); + STATIC_CHECK(iris::is_nothrow_directly_invocable_v); + STATIC_CHECK(iris::is_nothrow_directly_invocable::value); + + STATIC_CHECK(!iris::is_nothrow_directly_invocable_v); + STATIC_CHECK(iris::is_nothrow_directly_invocable_v); + // ReSharper disable once CppStaticAssertFailure + STATIC_CHECK(!iris::is_nothrow_directly_invocable_v); + + // ReSharper disable once CppStaticAssertFailure + STATIC_CHECK(!iris::is_nothrow_directly_invocable_v); + + STATIC_CHECK(iris::is_nothrow_directly_invocable_r_v); + STATIC_CHECK(!iris::is_nothrow_directly_invocable_r_v); + STATIC_CHECK(!iris::is_nothrow_directly_invocable_r_v); + + STATIC_CHECK(iris::is_nothrow_directly_invocable_r_v); + STATIC_CHECK(iris::is_nothrow_directly_invocable_r_v); + // ReSharper disable once CppStaticAssertFailure + STATIC_CHECK(!iris::is_nothrow_directly_invocable_r_v); + + STATIC_CHECK(iris::is_nothrow_directly_invocable_r_v); + STATIC_CHECK(!iris::is_nothrow_directly_invocable_r_v); + STATIC_CHECK(iris::is_directly_invocable_r_v); + STATIC_CHECK(!iris::is_nothrow_directly_invocable_r_v); + // ReSharper disable once CppStaticAssertFailure + STATIC_CHECK(!iris::is_nothrow_directly_invocable_r_v); + + STATIC_CHECK(!iris::is_nothrow_directly_invocable_r_v); + STATIC_CHECK(!iris::is_nothrow_directly_invocable_r_v); + STATIC_CHECK(!iris::is_nothrow_directly_invocable_r_v); + + STATIC_CHECK(iris::is_nothrow_directly_invocable_r::value); + STATIC_CHECK(std::conjunction_v< + iris::is_nothrow_directly_invocable_r, + iris::is_nothrow_directly_invocable_r + >); +}