On Tue, Sep 2, 2025 at 6:22 PM Luc Grosheintz <[email protected]>
wrote:

>
>
> On 9/2/25 13:45, Tomasz Kaminski wrote:
> > On Tue, Sep 2, 2025 at 10:07 AM Luc Grosheintz <[email protected]
> >
> > wrote:
> >
> >> This is a partial implementation of P2781R9. It adds std::cw and
> >> std::constant_wrapper, but doesn't modify __integral_constant_like for
> >> span/mdspan.
> >>
> >> libstdc++-v3/ChangeLog:
> >>
> >>          * include/bits/version.def (constant_wrapper): Add.
> >>          * include/bits/version.h: Regenerate.
> >>          * include/std/type_traits (_CwFixedValue): New class.
> >>          (_IndexSequence): New struct.
> >>          (_BuildIndexSequence): New struct.
> >>          (_ConstExprParam): New concept.
> >>          (_CwOperators): New struct.
> >>          (constant_wrapper): New struct.
> >>          (cw): New global constant.
> >>          * src/c++23/std.cc.in (constant_wrapper): Add.
> >>          (cw): Add.
> >>          * testsuite/20_util/constant_wrapper/adl.cc: New test.
> >>          * testsuite/20_util/constant_wrapper/ex.cc: New test.
> >>          * testsuite/20_util/constant_wrapper/generic.cc: New test.
> >>          * testsuite/20_util/constant_wrapper/instantiate.cc: New test.
> >>          * testsuite/20_util/constant_wrapper/op_comma_neg.cc: New test.
> >>          * testsuite/20_util/constant_wrapper/version.cc: New test.
> >>
> >> Signed-off-by: Luc Grosheintz <[email protected]>
> >>
> > Thanks, the implementation looks really solid. I have included decent
> > amount of suggestion
> > for the test, but this is mostly additional test cases.
> >
> > Generally, I would like to see additional test file, that would test with
> > other constant-wrapper like,
> > like integral_constant, and one defined in the test. Testing a few
> > operators should be fine.
> >
> > For binary operators, we should also check that adding runtime value
> (like
> > integer) produces
> > runtime result, and not constant_wrapper. Similarly a small separate test
> > should be fine.
> >
> > Finally, the assignment operators may work at runtime, if we have:
> > struct ConstAssignable // tuple<int&>
> > {
> >     ConstAssignable const& operator=(int) const
> >     { return *this; }
> > };
> >
> > cw<ConstAssignable> = 10; // would also compile and produce
> ConstAssignable.
> >
>
> Thank you. That clarified a lot of my questions. Can I delete adl.cc in
> v3? The reason is that instantiate.cc has mutated to a much stronger ADL
> test.
>
> I replied to one comment. Everything else is clear and will be updated.
>
> >
> > ---
> >>   libstdc++-v3/include/bits/version.def         |   8 +
> >>   libstdc++-v3/include/bits/version.h           |  10 +
> >>   libstdc++-v3/include/std/type_traits          | 371 +++++++++++
> >>   libstdc++-v3/src/c++23/std.cc.in              |   4 +
> >>   .../testsuite/20_util/constant_wrapper/adl.cc |  42 ++
> >>   .../testsuite/20_util/constant_wrapper/ex.cc  |  45 ++
> >>   .../20_util/constant_wrapper/generic.cc       | 252 ++++++++
> >>   .../20_util/constant_wrapper/instantiate.cc   | 575 ++++++++++++++++++
> >>   .../20_util/constant_wrapper/op_comma_neg.cc  |  14 +
> >>   .../20_util/constant_wrapper/version.cc       |  11 +
> >>   10 files changed, 1332 insertions(+)
> >>   create mode 100644
> libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc
> >>   create mode 100644
> libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc
> >>   create mode 100644
> >> libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc
> >>   create mode 100644
> >> libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc
> >>   create mode 100644
> >> libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc
> >>   create mode 100644
> >> libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc
> >>
> >> diff --git a/libstdc++-v3/include/bits/version.def
> >> b/libstdc++-v3/include/bits/version.def
> >> index 84c755da10e..4f8d50bca30 100644
> >> --- a/libstdc++-v3/include/bits/version.def
> >> +++ b/libstdc++-v3/include/bits/version.def
> >> @@ -393,6 +393,14 @@ ftms = {
> >>     };
> >>   };
> >>
> >> +ftms = {
> >> +  name = constant_wrapper;
> >> +  values = {
> >> +    v = 202506;
> >> +    cxxmin = 26;
> >> +  };
> >> +};
> >> +
> >>   ftms = {
> >>     name = has_unique_object_representations;
> >>     values = {
> >> diff --git a/libstdc++-v3/include/bits/version.h
> >> b/libstdc++-v3/include/bits/version.h
> >> index 410e3205339..2403584e57a 100644
> >> --- a/libstdc++-v3/include/bits/version.h
> >> +++ b/libstdc++-v3/include/bits/version.h
> >> @@ -430,6 +430,16 @@
> >>   #endif /* !defined(__cpp_lib_byte) && defined(__glibcxx_want_byte) */
> >>   #undef __glibcxx_want_byte
> >>
> >> +#if !defined(__cpp_lib_constant_wrapper)
> >> +# if (__cplusplus >  202302L)
> >> +#  define __glibcxx_constant_wrapper 202506L
> >> +#  if defined(__glibcxx_want_all) ||
> >> defined(__glibcxx_want_constant_wrapper)
> >> +#   define __cpp_lib_constant_wrapper 202506L
> >> +#  endif
> >> +# endif
> >> +#endif /* !defined(__cpp_lib_constant_wrapper) &&
> >> defined(__glibcxx_want_constant_wrapper) */
> >> +#undef __glibcxx_want_constant_wrapper
> >> +
> >>   #if !defined(__cpp_lib_has_unique_object_representations)
> >>   # if (__cplusplus >= 201703L) &&
> >> (defined(_GLIBCXX_HAVE_BUILTIN_HAS_UNIQ_OBJ_REP))
> >>   #  define __glibcxx_has_unique_object_representations 201606L
> >> diff --git a/libstdc++-v3/include/std/type_traits
> >> b/libstdc++-v3/include/std/type_traits
> >> index 4636457eb5a..26cbbb4fd5b 100644
> >> --- a/libstdc++-v3/include/std/type_traits
> >> +++ b/libstdc++-v3/include/std/type_traits
> >> @@ -41,6 +41,7 @@
> >>
> >>   #define __glibcxx_want_bool_constant
> >>   #define __glibcxx_want_bounded_array_traits
> >> +#define __glibcxx_want_constant_wrapper
> >>   #define __glibcxx_want_has_unique_object_representations
> >>   #define __glibcxx_want_integral_constant_callable
> >>   #define __glibcxx_want_is_aggregate
> >> @@ -4302,6 +4303,376 @@ template<typename _Ret, typename _Fn,
> typename...
> >> _Args>
> >>       };
> >>   #endif // C++11
> >>
> >> +#ifdef __cpp_lib_constant_wrapper // C++ >= 26
> >
> > I wonder if we should put it into a separate file. This would be useful
> if
> > we ever separate
> > type traits, and it does not seem to have any dependencies. Like
> > bits/constant_wrapper.h.
> > It will be also used by submdspan later.
> >
>
> Let me clarify: by "I wonder ..." do you mean:
>
>    - some time in the future, it would be nicer if this where in a
>    different file. Similar to Jonathans comment on PATCH 1/3;
>
>    - or please move these classes to <bits/constant_wrapper.h>?
>
Ah, sorry my mind got fixated on constant_wrapper being in <utility>,
and not type traits, where the move would make sense,

>
> If it's the former the rest of this block of text is irrelevant. If
> it's the latter: I see that you've merged the preparatory commit
> that moves the two helpers needed to implement constant_wrapper.
> To move the constant_wrapper related code to <bits/constant_wrapper.h>
> we need to also move the two helper classes _Index_tuple and
> _Build_index_tuple. Moving them to <bits/constant_wrapper.h> feels
> like creating about as much mess as we are cleaning up. Moving them to
> <bits/index_tuple.h> would run counter to the idea of preventing lots
> of very small files (which as Jonathan puts it "would be silly").
> Additionally, I don't think we can benefit from splitting the code
> into a separate header, because: a) we need to include it back into
> <type_traits> (the standard demands it); and b) we include
> <type_traits> from <array> which we include from <span> (which we
> include from <mdspan>). Given the contents of <type_traits> (true_type,
> is_*, enable_if, etc) I'm skeptical we can quickly break the
> dependency.
>
> TL;DR I'd like to suggest leaving it here for now, because we can't
> capitalize on moving the code.
>
I agree.

>
> >
> >
> > +  template<typename _Tp>
> >> +    struct _CwFixedValue
> >> +    {
> >> +      using _S_type = _Tp;
> >> +
> >> +      constexpr
> >> +      _CwFixedValue(_S_type __v) noexcept
> >> +      : _M_data(__v) { }
> >> +
> >> +      _S_type _M_data;
> >> +    };
> >> +
> >> +  template<typename _Tp, size_t _Extent>
> >> +    struct _CwFixedValue<_Tp[_Extent]>
> >> +    {
> >> +      using _S_type = _Tp[_Extent];
> >> +
> >> +      constexpr
> >> +      _CwFixedValue(_Tp (&__arr)[_Extent]) noexcept
> >> +        : _CwFixedValue(__arr, typename
> >> _Build_index_tuple<_Extent>::__type())
> >> +      { }
> >> +
> >> +      template<size_t... _Indices>
> >> +       constexpr
> >> +       _CwFixedValue(_Tp (&__arr)[_Extent], _Index_tuple<_Indices...>)
> >> noexcept
> >> +         : _M_data{__arr[_Indices]...}
> >> +       { }
> >> +
> >> +      _Tp _M_data[_Extent];
> >> +    };
> >> +
> >> +  template<typename _Tp, size_t _Extent>
> >> +    _CwFixedValue(_Tp (&)[_Extent]) -> _CwFixedValue<_Tp[_Extent]>;
> >> +
> >> +  template<_CwFixedValue _Tp,
> >> +          typename = typename decltype(_CwFixedValue(_Tp))::_S_type>
> >> +    struct constant_wrapper;
> >> +
> >> +  template<typename _Tp>
> >> +    concept _ConstExprParam = requires
> >> +    {
> >> +      typename constant_wrapper<_Tp::value>;
> >> +    };
> >> +
> >> +  struct _CwOperators
> >> +  {
> >> +    template<_ConstExprParam _Tp>
> >> +      friend constexpr auto
> >> +      operator+(_Tp) noexcept -> constant_wrapper<(+_Tp::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Tp>
> >> +      friend constexpr auto
> >> +      operator-(_Tp) noexcept -> constant_wrapper<(-_Tp::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Tp>
> >> +      friend constexpr auto
> >> +      operator~(_Tp) noexcept -> constant_wrapper<(~_Tp::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Tp>
> >> +      friend constexpr auto
> >> +      operator!(_Tp) noexcept -> constant_wrapper<(!_Tp::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Tp>
> >> +      friend constexpr auto
> >> +      operator&(_Tp) noexcept -> constant_wrapper<(&_Tp::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Tp>
> >> +      friend constexpr auto
> >> +      operator*(_Tp) noexcept -> constant_wrapper<(*_Tp::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator+(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value + _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator-(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value - _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator*(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value * _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator/(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value / _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator%(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value % _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator<<(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value << _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator>>(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value >> _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator&(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value & _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator|(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value | _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator^(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value ^ _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      requires (!is_constructible_v<bool, decltype(_Left::value)>
> >> +               || !is_constructible_v<bool, decltype(_Right::value)>)
> >> +      friend constexpr auto
> >> +      operator&&(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value && _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      requires (!is_constructible_v<bool, decltype(_Left::value)>
> >> +               || !is_constructible_v<bool, decltype(_Right::value)>)
> >> +      friend constexpr auto
> >> +      operator||(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value || _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator<=>(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value <=> _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator<(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value < _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator<=(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value <= _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator==(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value == _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator!=(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value != _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator>(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value > _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator>=(_Left, _Right) noexcept
> >> +       -> constant_wrapper<(_Left::value >= _Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator,(_Left, _Right) noexcept = delete;
> >> +
> >> +    template<_ConstExprParam _Left, _ConstExprParam _Right>
> >> +      friend constexpr auto
> >> +      operator->*(_Left, _Right) noexcept
> >> +       -> constant_wrapper<_Left::value->*(_Right::value)>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam... _Args>
> >> +      constexpr auto
> >> +      operator()(this _Tp, _Args...) noexcept
> >> +      requires
> >> +       requires(_Args...) {
> >> constant_wrapper<_Tp::value(_Args::value...)>(); }
> >> +      { return constant_wrapper<_Tp::value(_Args::value...)>{}; }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam... _Args>
> >> +      constexpr auto
> >> +      operator[](this _Tp, _Args...) noexcept
> >> +       -> constant_wrapper<(_Tp::value[_Args::value...])>
> >> +      { return {}; }
> >> +
> >> +    template<_ConstExprParam _Tp>
> >> +      constexpr auto
> >> +      operator++(this _Tp) noexcept
> >> +      requires requires(_Tp::value_type __x) { ++__x; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return ++__x; }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp>
> >> +      constexpr auto
> >> +      operator++(this _Tp, int) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x++; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x++; }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp>
> >> +      constexpr auto
> >> +      operator--(this _Tp) noexcept
> >> +      requires requires(_Tp::value_type __x) { --__x; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return --__x; }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp>
> >> +      constexpr auto
> >> +      operator--(this _Tp, int) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x--; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x--; }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator+=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x += _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x += _Right::value;
> }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator-=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x -= _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x -= _Right::value;
> }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator*=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x *= _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x *= _Right::value;
> }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator/=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x /= _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x /= _Right::value;
> }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator%=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x %= _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x %= _Right::value;
> }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator&=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x &= _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x &= _Right::value;
> }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator|=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x |= _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x |= _Right::value;
> }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator^=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x ^= _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x ^= _Right::value;
> }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator<<=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x <<= _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x <<= _Right::value;
> }()>{};
> >> +      }
> >> +
> >> +    template<_ConstExprParam _Tp, _ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator>>=(this _Tp, _Right) noexcept
> >> +      requires requires(_Tp::value_type __x) { __x >>= _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = _Tp::value; return __x >>= _Right::value;
> }()>{};
> >> +      }
> >> +  };
> >> +
> >> +  template<_CwFixedValue _X, typename>
> >> +  struct constant_wrapper : _CwOperators
> >> +  {
> >> +    static constexpr const auto& value = _X._M_data;
> >> +    using type = constant_wrapper;
> >> +    using value_type = typename decltype(_X)::_S_type;
> >> +
> >> +    template<_ConstExprParam _Right>
> >> +      constexpr auto
> >> +      operator=(_Right) const noexcept
> >> +      requires requires(value_type __x) { __x = _Right::value; }
> >> +      {
> >> +       return constant_wrapper<
> >> +         [] { auto __x = value; return __x = _Right::value; }()>{};
> >> +      }
> >> +
> >> +    constexpr
> >> +    operator decltype(auto)() const noexcept
> >> +    { return value; }
> >> +  };
> >> +
> >> +  template<_CwFixedValue _Tp>
> >> +    constexpr auto cw = constant_wrapper<_Tp>{};
> >> +#endif
> >> +
> >>     /// @} group metaprogramming
> >>
> >>   _GLIBCXX_END_NAMESPACE_VERSION
> >> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
> >> std.cc.in
> >> index 4888b8b4f23..a217a87330b 100644
> >> --- a/libstdc++-v3/src/c++23/std.cc.in
> >> +++ b/libstdc++-v3/src/c++23/std.cc.in
> >> @@ -2997,6 +2997,10 @@ export namespace std
> >>     using std::conditional_t;
> >>     using std::conjunction;
> >>     using std::conjunction_v;
> >> +#if __cpp_lib_constant_wrapper
> >> +  using std::constant_wrapper;
> >> +  using std::cw;
> >> +#endif
> >>     using std::decay;
> >>     using std::decay_t;
> >>     using std::disjunction;
> >> diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc
> >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc
> >> new file mode 100644
> >> index 00000000000..7ee754fe4b1
> >> --- /dev/null
> >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc
> >> @@ -0,0 +1,42 @@
> >> +// { dg-do run { target c++26 } }
> >> +#include <type_traits>
> >> +
> >> +#include <testsuite_hooks.h>
> >> +
> >> +namespace adl
> >> +{
> >> +  struct Addable
> >> +  {
> >> +    double x;
> >> +
> >> +    friend constexpr Addable
> >> +    operator+(Addable lhs, Addable rhs)
> >> +    { return Addable{lhs.x + rhs.x}; }
> >> +
> >> +    friend constexpr bool
> >> +    operator==(Addable lhs, Addable rhs)
> >> +    { return lhs.x == rhs.x; }
> >> +  };
> >> +}
> >> +
> >> +constexpr void
> >> +test_addable()
> >> +{
> >> +  auto check = [](auto a, auto b)
> >> +  {
> >> +    if constexpr (a + b == adl::Addable{5.0})
> >> +      return true;
> >> +    else
> >> +      return false;
> >> +  };
> >> +
> >> +  constexpr adl::Addable a{2.0}, b{3.0};
> >> +  VERIFY(check(std::cw<a>, std::cw<b>));
> >> +}
> >> +
> >> +int
> >> +main()
> >> +{
> >> +  test_addable();
> >> +  return 0;
> >> +}
> >> diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc
> >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc
> >> new file mode 100644
> >> index 00000000000..f46af929030
> >> --- /dev/null
> >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc
> >> @@ -0,0 +1,45 @@
> >> +// { dg-do run { target c++26 } }
> >>
> > This could be changed to dg-do compile, as we do not really care about
> > runtime value,
> > just final_phase compiling.
> >
> >> +#include <type_traits>
> >> +
> >> +#include <testsuite_hooks.h>
> >> +
> >> +constexpr auto
> >> +initial_phase(auto quantity_1, auto quantity_2)
> >> +{ return quantity_1 + quantity_2; }
> >> +
> >> +constexpr auto
> >> +middle_phase(auto tbd)
> >> +{ return tbd; }
> >> +
> >> +constexpr bool
> >> +final_phase(auto gathered, auto available)
> >> +{
> >> +  if constexpr (gathered == available)
> >> +    return true;
> >> +  else
> >> +    return false;
> >> +}
> >> +
> >> +void
> >> +impeccable_underground_planning()
> >> +{
> >> +  auto gathered_quantity = middle_phase(initial_phase(std::cw<42>,
> >> std::cw<13>));
> >> +  static_assert(gathered_quantity == 55);
> >> +  auto all_available = std::cw<55>;
> >> +  VERIFY(final_phase(gathered_quantity, all_available));
> >> +}
> >> +
> >> +// void
> >> +// deeply_flawed_underground_planning()
> >>
> > Then we could mark that we expect error from here.
> >
> >> +// {
> >> +//   constexpr auto gathered_quantity = middle_phase(initial_phase(42,
> >> 13));
> >> +//   constexpr auto all_available = 55;
> >> +//   final_phase(gathered_quantity, all_available);
> >> +// }
> >> +
> >> +int
> >> +main()
> >> +{
> >> +  impeccable_underground_planning();
> >> +  return 0;
> >> +}
> >> diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc
> >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc
> >> new file mode 100644
> >> index 00000000000..a74ce0b1d8b
> >> --- /dev/null
> >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc
> >> @@ -0,0 +1,252 @@
> >> +// { dg-do run { target c++26 } }
> >> +#include <type_traits>
> >> +#include <utility>
> >> +
> >> +#include <testsuite_hooks.h>
> >> +
> >> +constexpr void
> >> +test_c_arrays()
> >>
> > Could you add a test for string-literals, their support was a major
> reason
> > for having all the complications.
> >
> >> +{
> >> +  constexpr double x[] = {1.1, 2.2, 3.3};
> >> +  auto access = [](auto x, size_t i)
> >> +  { return x[i]; };
> >> +
> >> +  VERIFY(access(std::cw<x>, 0) == x[0]);
> >> +  VERIFY(access(std::cw<x>, 1) == x[1]);
> >> +  VERIFY(access(std::cw<x>, 2) == x[2]);
> >> +}
> >> +
> >> +constexpr void
> >> +test_ints()
> >> +{
> >> +  std::constant_wrapper<2> two;
> >> +  std::constant_wrapper<3> three;
> >> +  std::constant_wrapper<5> five;
> >> +
> >> +  VERIFY(two + 3 == 5);
> >> +  static_assert(std::same_as<decltype(two + 3), int>);
> >> +
> >> +  VERIFY(two + three == 5);
> >> +  VERIFY(two + three == five);
> >> +  static_assert(std::same_as<decltype(two + three),
> >> std::constant_wrapper<5>>);
> >> +
> >> +  VERIFY(two == std::cw<2>);
> >> +  VERIFY(two + 3 == std::cw<5>);
> >> +}
> >> +
> >> +constexpr int
> >> +add(int i, int j)
> >> +{ return i + j; }
> >> +
> >> +struct Add
> >> +{
> >> +  constexpr int
> >> +  operator()(int i, int j) const noexcept
> >> +  { return i + j; }
> >> +};
> >> +
> >> +constexpr void
> >> +test_function_object()
> >> +{
> >> +  auto cadd = std::cw<Add{}>;
> >> +  auto ci = std::cw<2>;
> >> +  auto cj = std::cw<3>;
> >> +
> >> +  VERIFY(cadd(ci, cj) == 5);
> >> +  static_assert(std::same_as<decltype(cadd(ci, cj)),
> >> std::constant_wrapper<5>>);
> >> +
> >> +  // Invalid:
> >> +  // VERIFY(cadd(2, cj) == 5);
> >> +  // VERIFY(cadd(2, 3) == 5);
> >>
> > Please add negative tests for this, they can be done by static asserting
> > that cadd is not invokable.
> >
> >> +}
> >> +
> >> +constexpr void
> >> +test_function_pointer()
> >>
> > +{
> >> +  auto cptr = std::cw<add>;
> >> +  auto ci = std::cw<2>;
> >> +  auto cj = std::cw<3>;
> >> +
> >> +  VERIFY(cptr(ci, cj) == 5);
> >> +  static_assert(std::same_as<decltype(cptr(ci, cj)),
> >> std::constant_wrapper<5>>);
> >> +
> >> +  VERIFY(cptr(2, cj) == 5);
> >> +  static_assert(std::same_as<decltype(cptr(2, cj)), int>);
> >> +
> >> +  VERIFY(cptr(2, 3) == 5);
> >> +  static_assert(std::same_as<decltype(cptr(2, 3)), int>);
> >>
> > Similar here, test that operator[] does not work with  runtime values.
> >
> >> +}
> >> +
> >> +struct Indexable1
> >> +{
> >> +  constexpr int
> >> +  operator[](int i, int j) const noexcept
> >> +  { return i*j; }
> >> +};
> >> +
> >> +constexpr void
> >> +test_indexable1()
> >> +{
> >> +  auto cind = std::cw<Indexable1{}>;
> >> +  auto ci = std::cw<2>;
> >> +  auto cj = std::cw<3>;
> >> +  VERIFY(cind[ci, cj] == ci*cj);
> >> +  static_assert(std::same_as<decltype(cind[ci, cj]),
> >> std::constant_wrapper<6>>);
> >> +}
> >> +
> >> +struct Indexable2
> >> +{
> >> +  template<typename I, typename J>
> >> +    constexpr int
> >> +    operator[](I i, J j) const noexcept
> >> +    { return i*j; }
> >> +};
> >> +
> >> +constexpr void
> >> +test_indexable2()
> >> +{
> >> +  auto cind = std::cw<Indexable2{}>;
> >> +  auto ci = std::cw<2>;
> >> +  auto cj = std::cw<3>;
> >> +  VERIFY(cind[ci, cj] == ci*cj);
> >> +  static_assert(std::same_as<decltype(cind[ci, cj]),
> >> std::constant_wrapper<6>>);
> >> +}
> >> +
> >> +struct Indexable3
> >> +{
> >> +  template<typename... Is>
> >> +    constexpr int
> >> +    operator[](Is... i) const noexcept
> >> +    { return (1 * ... * i); }
> >> +};
> >> +
> >> +constexpr void
> >> +test_indexable3()
> >> +{
> >> +  auto cind = std::cw<Indexable3{}>;
> >> +  auto ci = std::cw<2>;
> >> +  auto cj = std::cw<3>;
> >> +  VERIFY(cind[] == 1);
> >> +  static_assert(std::same_as<decltype(cind[]),
> std::constant_wrapper<1>>);
> >> +  VERIFY(cind[ci] == ci);
> >> +  static_assert(std::same_as<decltype(cind[ci]),
> >> std::constant_wrapper<2>>);
> >> +  VERIFY(cind[ci, cj] == ci*cj);
> >> +  static_assert(std::same_as<decltype(cind[ci, cj]),
> >> std::constant_wrapper<6>>);
> >> +}
> >> +
> >> +struct Divide
> >> +{
> >> +  int value;
> >> +
> >> +  constexpr int
> >> +  divide(int div) const
> >> +  { return value / div; }
> >> +
> >> +};
> >> +
> >> +constexpr void
> >> +test_member_pointer()
> >> +{
> >> +  auto cdiv = std::cw<&Divide::divide>;
> >> +  auto co = std::cw<Divide{42}>;
> >> +  VERIFY(((&co)->* cdiv)(3) == co.value.value / 3);
> >>
> > I would prefer if we would also use  static_assert to check that
> > constant_wrapper is produced.
> > I think the following should also work and produce int.
> >    (&co)->*(&Divide::divide)
> > Similary:
> >    &co::value->cdiv
> >
> >> +}
> >> +
> >> +constexpr void
> >> +test_pseudo_mutator()
> >> +{
> >> +  auto ci = std::cw<3>;
> >> +  auto cmmi = --ci;
> >> +  VERIFY(ci.value == 3);
> >> +  VERIFY(cmmi.value == 2);
> >> +
> >> +  auto cimm = ci--;
> >> +  VERIFY(ci.value == 3);
> >> +  VERIFY(cimm.value == 3);
> >> +}
> >> +
> >> +struct Truthy
> >> +{
> >> +  constexpr operator bool() const
> >> +  { return true; }
> >> +};
> >> +
> >> +constexpr void
> >> +test_logic()
> >> +{
> >> +  auto ctrue = std::cw<true>;
> >> +  auto cfalse = std::cw<false>;
> >> +  auto truthy = Truthy{};
> >> +
> >> +  VERIFY(ctrue && ctrue);
> >>
> > Please also add checks for the type of expression, the above should be
> > constant
> > wrapper. The remaining to not.
> >
> >> +  VERIFY(ctrue || cfalse);
> >> +  VERIFY(truthy && true);
> >> +  VERIFY((std::cw<0> < std::cw<1>) && (std::cw<1> < std::cw<5>));
> >> +
> >> +  // auto ctruthy = std::cw<Truthy{}>;
> >>
> > It would be good to put them in a negative test, as we test for specific
> > constraint.
> > I mean static_assert on concept should be fine.
> >
> >> +  // Invalid:
> >> +  // VERIFY(ctrue && ctruthy);
> >> +  // VERIFY(true && ctruthy);
> >>
> > +}
> >> +
> >> +struct ThreeWayComp
> >> +{
> >> +  friend
> >> +  constexpr std::strong_ordering
> >> +  operator<=>(ThreeWayComp lhs, ThreeWayComp rhs)
> >> +  { return lhs.value <=> rhs.value; }
> >> +
> >> +  int value;
> >> +};
> >> +
> >> +constexpr void
> >> +test_three_way()
> >> +{
> >> +  VERIFY(std::cw<ThreeWayComp{0}> < std::cw<ThreeWayComp{1}>);
> >> +  VERIFY(std::cw<ThreeWayComp{2}> > std::cw<ThreeWayComp{1}>);
> >> +  VERIFY(std::cw<ThreeWayComp{2}> >= std::cw<ThreeWayComp{1}>);
> >> +  VERIFY(std::cw<ThreeWayComp{0}> <= std::cw<ThreeWayComp{1}>);
> >>
> > Test also with runtime values.
> >
> >> +}
> >> +
> >> +struct EqualityComp
> >> +{
> >> +  friend
> >> +  constexpr bool
> >> +  operator==(EqualityComp lhs, EqualityComp rhs)
> >> +  { return lhs.value == rhs.value; }
> >> +
> >> +  int value;
> >> +};
> >> +
> >> +constexpr void
> >> +test_equality()
> >> +{
> >> +  VERIFY(std::cw<EqualityComp{1}> == std::cw<EqualityComp{1}>);
> >> +  VERIFY(std::cw<EqualityComp{0}> != std::cw<EqualityComp{1}>);
> >>
> > Similar here,  test comparing against runtime values.
> >
> >> +}
> >> +
> >> +constexpr bool
> >> +test_all()
> >> +{
> >> +  test_c_arrays();
> >> +  test_ints();
> >> +  test_function_object();
> >> +  test_function_pointer();
> >> +  test_indexable1();
> >> +  test_indexable2();
> >> +  test_indexable3();
> >> +  test_member_pointer();
> >> +  test_pseudo_mutator();
> >> +  test_logic();
> >> +  test_three_way();
> >> +  test_equality();
> >> +  return true;
> >> +}
> >> +
> >> +int
> >> +main()
> >> +{
> >> +  test_all();
> >> +  static_assert(test_all());
> >> +  return 0;
> >> +}
> >> diff --git
> >> a/libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc
> >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc
> >> new file mode 100644
> >> index 00000000000..4f1232598d6
> >> --- /dev/null
> >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc
> >> @@ -0,0 +1,575 @@
> >> +// { dg-do run { target c++26 } }
> >> +#include <type_traits>
> >> +#include <utility>
> >> +
> >> +#include <testsuite_hooks.h>
> >> +
> >> +namespace free_ops
> >> +{
> >> +  template<int OpId>
> >> +    struct UnaryOps
> >> +    {
> >> +      friend constexpr int
> >> +      operator+(UnaryOps) noexcept requires (OpId == 0)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator-(UnaryOps) noexcept requires (OpId == 1)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator~(UnaryOps) noexcept requires (OpId == 2)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator!(UnaryOps) noexcept requires (OpId == 3)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator&(UnaryOps) noexcept requires (OpId == 4)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator*(UnaryOps) noexcept requires (OpId == 5)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator++(UnaryOps) noexcept requires (OpId == 6)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator++(UnaryOps, int) noexcept requires (OpId == 7)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator--(UnaryOps) noexcept requires (OpId == 8)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator--(UnaryOps, int) noexcept requires (OpId == 9)
> >> +      { return OpId; }
> >> +    };
> >> +}
> >> +
> >> +namespace member_ops
> >> +{
> >> +  template<int OpId>
> >> +    struct UnaryOps
> >> +    {
> >> +      constexpr int
> >> +      operator+() const noexcept requires (OpId == 0)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator-() const noexcept requires (OpId == 1)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator~() const noexcept requires (OpId == 2)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator!() const noexcept requires (OpId == 3)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator&() const noexcept requires (OpId == 4)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator*() const noexcept requires (OpId == 5)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator++() const noexcept requires (OpId == 6)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator++(int) const noexcept requires (OpId == 7)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator--() const noexcept requires (OpId == 8)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator--(int) const noexcept requires (OpId == 9)
> >> +      { return OpId; }
> >> +    };
> >> +}
> >> +
> >> +constexpr size_t n_unary_ops = 10;
> >> +
> >> +template<template<int> typename Ops, int OpId>
> >> +  constexpr void
> >> +  test_unary_operator()
> >> +  {
> >> +    auto x = std::cw<Ops<OpId>{}>;
> >> +
> >> +    auto check = [](auto c)
> >> +    {
> >> +      VERIFY(c == OpId);
> >> +      static_assert(std::same_as<decltype(c),
> >> std::constant_wrapper<OpId>>);
> >> +    };
> >> +
> >> +    if constexpr (OpId == 0)
> >> +      check(+x);
> >> +    if constexpr (OpId == 1)
> >> +      check(-x);
> >> +    if constexpr (OpId == 2)
> >> +      check(~x);
> >> +    if constexpr (OpId == 3)
> >> +      check(!x);
> >> +    if constexpr (OpId == 4)
> >> +      check(&x);
> >> +    if constexpr (OpId == 5)
> >> +      check(*x);
> >> +    if constexpr (OpId == 6)
> >> +      check(++x);
> >> +    if constexpr (OpId == 7)
> >> +      check(x++);
> >> +    if constexpr (OpId == 8)
> >> +      check(--x);
> >> +    if constexpr (OpId == 9)
> >> +      check(x--);
> >> +
> >> +    static_assert(n_unary_ops == 10);
> >> +  }
> >> +
> >> +constexpr void
> >> +test_unary_operators_all()
> >> +{
> >> +  auto run = []<size_t... Idx>(std::integer_sequence<size_t, Idx...>)
> >> +  {
> >> +    (test_unary_operator<free_ops::UnaryOps, Idx>(), ...);
> >> +    (test_unary_operator<member_ops::UnaryOps, Idx>(), ...);
> >> +  };
> >> +  run(std::make_index_sequence<n_unary_ops>());
> >> +}
> >> +
> >> +namespace free_ops
> >> +{
> >> +  template<int OpId>
> >> +    struct BinaryOps
> >> +    {
> >> +      friend constexpr int
> >> +      operator+(BinaryOps, BinaryOps) noexcept requires (OpId == 0)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator-(BinaryOps, BinaryOps) noexcept requires (OpId == 1)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator*(BinaryOps, BinaryOps) noexcept requires (OpId == 2)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator/(BinaryOps, BinaryOps) noexcept requires (OpId == 3)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator%(BinaryOps, BinaryOps) noexcept requires (OpId == 4)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator<<(BinaryOps, BinaryOps) noexcept requires (OpId == 5)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator>>(BinaryOps, BinaryOps) noexcept requires (OpId == 6)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator&(BinaryOps, BinaryOps) noexcept requires (OpId == 7)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator|(BinaryOps, BinaryOps) noexcept requires (OpId == 8)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator^(BinaryOps, BinaryOps) noexcept requires (OpId == 9)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator&&(BinaryOps, BinaryOps) noexcept requires (OpId == 10)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator||(BinaryOps, BinaryOps) noexcept requires (OpId == 11)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator<=>(BinaryOps, BinaryOps) noexcept requires (OpId == 12)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator<(BinaryOps, BinaryOps) noexcept requires (OpId == 13)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator<=(BinaryOps, BinaryOps) noexcept requires (OpId == 14)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator==(BinaryOps, BinaryOps) noexcept requires (OpId == 15)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator!=(BinaryOps, BinaryOps) noexcept requires (OpId == 16)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator>(BinaryOps, BinaryOps) noexcept requires (OpId == 17)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator>=(BinaryOps, BinaryOps) noexcept requires (OpId == 18)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator+=(BinaryOps, BinaryOps) noexcept requires (OpId == 19)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator-=(BinaryOps, BinaryOps) noexcept requires (OpId == 20)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator*=(BinaryOps, BinaryOps) noexcept requires (OpId == 21)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator/=(BinaryOps, BinaryOps) noexcept requires (OpId == 22)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator%=(BinaryOps, BinaryOps) noexcept requires (OpId == 23)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator&=(BinaryOps, BinaryOps) noexcept requires (OpId == 24)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator|=(BinaryOps, BinaryOps) noexcept requires (OpId == 25)
> >> +      { return OpId; }
> >> +      friend constexpr int
> >> +
> >> +      operator^=(BinaryOps, BinaryOps) noexcept requires (OpId == 26)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator<<=(BinaryOps, BinaryOps) noexcept requires (OpId == 27)
> >> +      { return OpId; }
> >> +
> >> +      friend constexpr int
> >> +      operator>>=(BinaryOps, BinaryOps) noexcept requires (OpId == 28)
> >> +      { return OpId; }
> >> +    };
> >> +}
> >> +
> >> +namespace member_ops
> >> +{
> >> +  template<int OpId>
> >> +    struct BinaryOps
> >> +    {
> >> +      constexpr int
> >> +      operator+(BinaryOps) const noexcept requires (OpId == 0)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator-(BinaryOps) const noexcept requires (OpId == 1)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator*(BinaryOps) const noexcept requires (OpId == 2)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator/(BinaryOps) const noexcept requires (OpId == 3)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator%(BinaryOps) const noexcept requires (OpId == 4)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator<<(BinaryOps) const noexcept requires (OpId == 5)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator>>(BinaryOps) const noexcept requires (OpId == 6)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator&(BinaryOps) const noexcept requires (OpId == 7)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator|(BinaryOps) const noexcept requires (OpId == 8)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator^(BinaryOps) const noexcept requires (OpId == 9)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator&&(BinaryOps) const noexcept requires (OpId == 10)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator||(BinaryOps) const noexcept requires (OpId == 11)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator<=>(BinaryOps) const noexcept requires (OpId == 12)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator<(BinaryOps) const noexcept requires (OpId == 13)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator<=(BinaryOps) const noexcept requires (OpId == 14)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator==(BinaryOps) const noexcept requires (OpId == 15)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator!=(BinaryOps) const noexcept requires (OpId == 16)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator>(BinaryOps) const noexcept requires (OpId == 17)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator>=(BinaryOps) const noexcept requires (OpId == 18)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator+=(BinaryOps) const noexcept requires (OpId == 19)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator-=(BinaryOps) const noexcept requires (OpId == 20)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator*=(BinaryOps) const noexcept requires (OpId == 21)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator/=(BinaryOps) const noexcept requires (OpId == 22)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator%=(BinaryOps) const noexcept requires (OpId == 23)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator&=(BinaryOps) const noexcept requires (OpId == 24)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator|=(BinaryOps) const noexcept requires (OpId == 25)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator^=(BinaryOps) const noexcept requires (OpId == 26)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator<<=(BinaryOps) const noexcept requires (OpId == 27)
> >> +      { return OpId; }
> >> +
> >> +      constexpr int
> >> +      operator>>=(BinaryOps) const noexcept requires (OpId == 28)
> >> +      { return OpId; }
> >> +    };
> >> +}
> >> +
> >> +constexpr size_t n_binary_ops = 29;
> >> +
> >> +template<template<int> typename Ops, int OpId>
> >> +  constexpr void
> >> +  test_binary_operator()
> >> +  {
> >> +    auto cx = std::cw<Ops<OpId>{}>;
> >> +    auto cy = std::cw<Ops<OpId>{}>;
> >> +
> >> +    auto check = [](auto c)
> >> +    {
> >> +      VERIFY(c == OpId);
> >> +      static_assert(std::same_as<decltype(c),
> >> std::constant_wrapper<OpId>>);
> >> +    };
> >> +
> >> +    if constexpr (OpId == 0)
> >> +      check(cx + cy);
> >> +    if constexpr (OpId == 1)
> >> +      check(cx - cy);
> >> +    if constexpr (OpId == 2)
> >> +      check(cx * cy);
> >> +    if constexpr (OpId == 3)
> >> +      check(cx / cy);
> >> +    if constexpr (OpId == 4)
> >> +      check(cx % cy);
> >> +    if constexpr (OpId == 5)
> >> +      check(cx << cy);
> >> +    if constexpr (OpId == 6)
> >> +      check(cx >> cy);
> >> +    if constexpr (OpId == 7)
> >> +      check(cx & cy);
> >> +    if constexpr (OpId == 8)
> >> +      check(cx | cy);
> >> +    if constexpr (OpId == 10)
> >> +      check(cx && cy);
> >> +    if constexpr (OpId == 11)
> >> +      check(cx || cy);
> >> +    if constexpr (OpId == 12)
> >> +      check(cx <=> cy);
> >> +    if constexpr (OpId == 13)
> >> +      check(cx < cy);
> >> +    if constexpr (OpId == 14)
> >> +      check(cx <= cy);
> >> +    if constexpr (OpId == 15)
> >> +      check(cx == cy);
> >> +    if constexpr (OpId == 16)
> >> +      check(cx != cy);
> >> +    if constexpr (OpId == 17)
> >> +      check(cx > cy);
> >> +    if constexpr (OpId == 18)
> >> +      check(cx >= cy);
> >> +    if constexpr (OpId == 19)
> >> +      check(cx += cy);
> >> +    if constexpr (OpId == 20)
> >> +      check(cx -= cy);
> >> +    if constexpr (OpId == 21)
> >> +      check(cx *= cy);
> >> +    if constexpr (OpId == 22)
> >> +      check(cx /= cy);
> >> +    if constexpr (OpId == 23)
> >> +      check(cx %= cy);
> >> +    if constexpr (OpId == 24)
> >> +      check(cx &= cy);
> >> +    if constexpr (OpId == 25)
> >> +      check(cx |= cy);
> >> +    if constexpr (OpId == 26)
> >> +      check(cx ^= cy);
> >> +    if constexpr (OpId == 27)
> >> +      check(cx <<= cy);
> >> +    if constexpr (OpId == 28)
> >> +      check(cx >>= cy);
> >> +    static_assert(n_binary_ops == 29);
> >> +  }
> >> +
> >> +template<template<int> typename Ops, int OpId>
> >> +  constexpr void
> >> +  test_mixed_binary_operators()
> >> +  {
> >> +    constexpr auto x = Ops<OpId>{};
> >> +    auto cx = std::cw<x>;
> >> +    constexpr auto y = Ops<OpId>{};
> >> +    auto cy = std::cw<y>;
> >> +
> >> +    auto check = [](auto vc, auto cv)
> >> +    {
> >> +      auto impl = [](auto c)
> >> +      {
> >> +       VERIFY(c == OpId);
> >> +       static_assert(std::same_as<decltype(c), int>);
> >> +      };
> >> +
> >> +      impl(vc);
> >> +      impl(cv);
> >> +    };
> >> +
> >> +    if constexpr (OpId == 0)
> >> +      check(x + cy, cx + y);
> >> +    if constexpr (OpId == 1)
> >> +      check(x - cy, cx - y);
> >> +    if constexpr (OpId == 2)
> >> +      check(x * cy, cx * y);
> >> +    if constexpr (OpId == 3)
> >> +      check(x / cy, cx / y);
> >> +    if constexpr (OpId == 4)
> >> +      check(x % cy, cx % y);
> >> +    if constexpr (OpId == 5)
> >> +      check(x << cy, cx << y);
> >> +    if constexpr (OpId == 6)
> >> +      check(x >> cy, cx >> y);
> >> +    if constexpr (OpId == 7)
> >> +      check(x & cy, cx & y);
> >> +    if constexpr (OpId == 8)
> >> +      check(x | cy, cx | y);
> >> +    if constexpr (OpId == 10)
> >> +      check(x && cy, cx && y);
> >> +    if constexpr (OpId == 11)
> >> +      check(x || cy, cx || y);
> >> +    if constexpr (OpId == 12)
> >> +      check(x <=> cy, cx <=> y);
> >> +    if constexpr (OpId == 13)
> >> +      check(x < cy, cx < y);
> >> +    if constexpr (OpId == 14)
> >> +      check(x <= cy, cx <= y);
> >> +    if constexpr (OpId == 15)
> >> +      check(x == cy, cx == y);
> >> +    if constexpr (OpId == 16)
> >> +      check(x != cy, cx != y);
> >> +    if constexpr (OpId == 17)
> >> +      check(x > cy, cx > y);
> >> +    if constexpr (OpId == 18)
> >> +      check(x >= cy, cx >= y);
> >> +    if constexpr (OpId == 19)
> >> +      check(x += cy, cx += y);
> >> +    if constexpr (OpId == 20)
> >> +      check(x -= cy, cx -= y);
> >> +    if constexpr (OpId == 21)
> >> +      check(x *= cy, cx *= y);
> >> +    if constexpr (OpId == 22)
> >> +      check(x /= cy, cx /= y);
> >> +    if constexpr (OpId == 23)
> >> +      check(x %= cy, cx %= y);
> >> +    if constexpr (OpId == 24)
> >> +      check(x &= cy, cx &= y);
> >> +    if constexpr (OpId == 25)
> >> +      check(x |= cy, cx |= y);
> >> +    if constexpr (OpId == 26)
> >> +      check(x ^= cy, cx ^= y);
> >> +    if constexpr (OpId == 27)
> >> +      check(x <<= cy, cx <<= y);
> >> +    if constexpr (OpId == 28)
> >> +      check(x >>= cy, cx >>= y);
> >> +    static_assert(n_binary_ops == 29);
> >> +  }
> >> +
> >> +constexpr void
> >> +test_binary_operators_all()
> >> +{
> >> +  auto run = []<size_t... Idx>(std::integer_sequence<size_t, Idx...>)
> >> +  {
> >> +    (test_binary_operator<free_ops::BinaryOps, Idx>(), ...);
> >> +    (test_mixed_binary_operators<free_ops::BinaryOps, Idx>(), ...);
> >> +    (test_binary_operator<member_ops::BinaryOps, Idx>(), ...);
> >> +  };
> >> +  run(std::make_index_sequence<n_binary_ops>());
> >> +}
> >> +
> >> +constexpr bool
> >> +test_all()
> >> +{
> >> +  test_unary_operators_all();
> >> +  test_binary_operators_all();
> >> +  return true;
> >> +}
> >> +
> >> +int
> >> +main()
> >> +{
> >> +  test_all();
> >> +  return 0;
> >> +}
> >> diff --git
> >> a/libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc
> >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc
> >> new file mode 100644
> >> index 00000000000..4384e920ea5
> >> --- /dev/null
> >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc
> >> @@ -0,0 +1,14 @@
> >> +// { dg-do compile { target c++26 } }
> >> +#include <type_traits>
> >> +
> >> +constexpr void
> >> +test_comma_same_types()
> >> +{
> >> +  (std::cw<1>, std::cw<2>); // { dg-error "use of deleted function" }
> >> +}
> >> +
> >> +constexpr void
> >> +test_comma_different_types()
> >> +{
> >> +  (std::cw<1>, std::cw<2.0>); // { dg-error "use of deleted function" }
> >> +}
> >> diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc
> >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc
> >> new file mode 100644
> >> index 00000000000..4fee6159141
> >> --- /dev/null
> >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc
> >> @@ -0,0 +1,11 @@
> >> +// { dg-do preprocess { target c++26 } }
> >> +// { dg-add-options no_pch }
> >> +
> >> +#include <type_traits>
> >> +
> >> +#ifndef __cpp_lib_constant_wrapper
> >> +#error "Feature test macro __cpp_lib_constant_wrapper is missing for
> >> <type_traits>"
> >> +#if __cpp_lib_constant_wrapper < 202506L
> >> +#error "Feature test macro __cpp_lib_constant_wrapper has the wrong
> value"
> >> +#endif
> >> +#endif
> >> --
> >> 2.51.0
> >>
> >>
> >
>
>

Reply via email to