On Thu, Sep 4, 2025 at 3:37 PM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:

>
>
> On 9/4/25 2:20 PM, Luc Grosheintz 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.
> >
> > Co-authored-by: Tomasz Kamiński <tkami...@redhat.com>
> > Signed-off-by: Tomasz Kamiński <tkami...@redhat.com>
> > Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
> > ---
> >   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 |  86 +++
> >   .../testsuite/20_util/constant_wrapper/ex.cc  |  39 ++
> >   .../20_util/constant_wrapper/generic.cc       | 386 ++++++++++++
> >   .../20_util/constant_wrapper/instantiate.cc   | 575 ++++++++++++++++++
> >   .../20_util/constant_wrapper/op_comma_neg.cc  |  14 +
> >   .../constant_wrapper/other_wrappers.cc        |  75 +++
> >   .../20_util/constant_wrapper/version.cc       |  11 +
> >   11 files changed, 1579 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/other_wrappers.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 8707a123109..65b9a278776 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 c7569f42773..b05249857d2 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
> > +  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..3cdc09c1eb7
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc
> > @@ -0,0 +1,86 @@
> > +// { dg-do compile { target c++26 } }
> > +#include <type_traits>
> > +#include <concepts>
> > +
> > +#include <testsuite_hooks.h>
> > +
> > +namespace adl {
> > +
> > +struct Friend
> > +{};
> > +
> > +constexpr
> > +int operator+(Friend, int x)
> > +{ return x; };
> > +
> > +template<typename T>
> > +  struct TemplFriend
> > +  { };
> > +
> > +template<typename T>
> > +  constexpr
> > +  // templated, we cannot deduce T from cw<Friend<int>>
> > +  int operator+(TemplFriend<T>, int x)
> > +  { return x; };
> > +
> > +
> > +struct HiddenFriend
> > +{
> > +  constexpr friend
> > +  int operator+(HiddenFriend, int x)
> > +  { return x; }
> > +};
> > +
> > +template<typename T>
> > +  struct TemplHiddenFriend
> > +  {
> > +    constexpr friend
> > +    // note that this not not template itself
> > +    int operator+(TemplHiddenFriend, int x)
> > +    { return x; }
> > +  };
> > +}
> > +
> > +template<typename T>
> > +  concept supportMixedObj = requires
> > +  {
> > +    { std::cw<T{}> + 1 } -> std::same_as<int>;
> > +  };
> > +
> > +template<typename T>
> > +  concept supportMixedInt = requires(T t)
> > +  {
> > +    { t + std::cw<1> } -> std::same_as<int>;
> > +  };
> > +
> > +static_assert(supportMixedObj<adl::Friend>);
> > +static_assert(supportMixedInt<adl::Friend>);
> > +static_assert(!supportMixedObj<adl::TemplFriend<int>>);
> > +static_assert(supportMixedInt<adl::TemplFriend<int>>);
> > +
> > +static_assert(supportMixedObj<adl::HiddenFriend>);
> > +static_assert(supportMixedInt<adl::HiddenFriend>);
> > +static_assert(supportMixedObj<adl::TemplHiddenFriend<int>>);
> > +static_assert(supportMixedInt<adl::TemplHiddenFriend<int>>);
> > +
> > +struct Member
> > +{
> > +  constexpr
> > +  // conversion for the first argument is not allowed
> > +  int operator+(int x) const
> > +  { return x; }
> > +};
> > +
> > +static_assert(!supportMixedObj<Member>);
> > +static_assert(supportMixedInt<Member>);
> > +
> > +struct ExplicitThisMember
> > +{
> > +  constexpr
> > +  // conversion for the first argument is not allowed
> > +  int operator+(this ExplicitThisMember, int x)
> > +  { return x; }
> > +};
> > +
> > +static_assert(!supportMixedObj<ExplicitThisMember>);
> > +static_assert(supportMixedInt<ExplicitThisMember>);
> > 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..a4d967b9634
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc
> > @@ -0,0 +1,39 @@
> > +// { dg-do compile { target c++26 } }
> > +#include <type_traits>
> > +#include <iostream>
> > +
> > +#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 void
> > +final_phase(auto gathered, auto available)
> > +{
> > +  if constexpr (gathered == available)
> > +    std::cout << "Profit!\n";
> > +}
> > +
> > +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>;
> > +  final_phase(gathered_quantity, all_available);
> > +}
> > +
> > +void
> > +deeply_flawed_underground_planning()
> > +{
> > +  constexpr auto gathered_quantity = middle_phase(initial_phase(42,
> 13));
> > +  constexpr auto all_available = 55;
> > +  final_phase(gathered_quantity, all_available);  // { dg-error
> "required from here" }
> > +}
> > +
> > +// { dg-prune-output "'gathered' is not a constant expression" }
> > 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..75ee6abb3e5
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc
> > @@ -0,0 +1,386 @@
> > +// { dg-do run { target c++26 } }
> > +#include <type_traits>
> > +#include <utility>
> > +#include <string_view>
> > +
> > +#include <testsuite_hooks.h>
> > +
> > +constexpr void
> > +check_same(auto actual, auto expected)
> > +{
> > +  VERIFY(actual == expected);
> > +  static_assert(std::same_as<decltype(actual), decltype(expected)>);
> > +};
> > +
> > +
> > +constexpr void
> > +test_c_arrays()
> > +{
> > +  constexpr double x[] = {1.1, 2.2, 3.3};
> > +  auto cx = std::cw<x>;
> > +  auto access = [](auto x, size_t i)
> > +  { return x[i]; };
> > +
> > +  check_same(access(std::cw<x>, 0), x[0]);
> > +  check_same(access(std::cw<x>, 1), x[1]);
> > +  check_same(access(std::cw<x>, 2), x[2]);
> > +
> > +  check_same(cx[std::cw<0>], std::cw<x[0]>);
> > +  check_same(cx[std::cw<1>], std::cw<x[1]>);
> > +  check_same(cx[std::cw<2>], std::cw<x[2]>);
> > +}
> > +
> > +constexpr size_t
> > +deduce_cstr_size(auto str)
> > +{
> > +  size_t sz = 0;
> > +  while(str[sz++] != '\0') { }
> > +  return sz;
> > +}
> > +
> > +constexpr void
> > +test_string_literals()
> > +{
> > +  auto foo = std::cw<"foo">;
> > +  constexpr const typename decltype(foo)::value_type & cstr = foo;
> > +  constexpr size_t N = std::size(cstr);
> > +  constexpr auto foo_view = std::string_view(cstr, N-1);
> > +
> > +  constexpr const char (&cstr1)[deduce_cstr_size(foo)] = foo;
> > +  constexpr size_t N1 = std::size(cstr);
> > +
> > +  static_assert(static_cast<char const*>(cstr) ==
> > +             static_cast<char const*>(cstr1));
> > +  static_assert(N1 == N);
> > +
> > +  static_assert(foo[0] == 'f');
> > +  static_assert(foo[1] == 'o');
> > +  static_assert(foo[2] == 'o');
> > +  static_assert(foo[3] == '\0');
> > +  static_assert(static_cast<char const *>(foo) == foo_view);
> > +}
> > +
> > +constexpr bool
> > +convert_constexpr(auto c)
> > +{
> > +  if constexpr (int(c) > 0)
> > +    return true;
> > +  return false;
> > +}
> > +
> > +constexpr void
> > +test_converted_constexpr()
> > +{
> > +  VERIFY(!convert_constexpr(std::cw<-1>));
> > +  VERIFY(convert_constexpr(std::cw<1>));
> > +}
> > +
> > +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 check = [](auto cfo)
> > +  {
> > +    auto ci = std::cw<2>;
> > +    auto cj = std::cw<3>;
> > +
> > +    VERIFY(cfo(ci, cj) == 5);
> > +    static_assert(std::same_as<decltype(cfo(ci, cj)),
> std::constant_wrapper<5>>);
> > +
> > +    static_assert(std::invocable<decltype(cfo), decltype(ci),
> decltype(cj)>);
> > +    static_assert(!std::invocable<decltype(cfo), int, decltype(cj)>);
> > +    static_assert(!std::invocable<decltype(cfo), int, int>);
> > +  };
> > +
> > +  check(std::cw<Add{}>);
> > +  check(std::cw<[](int i, int j){ return i + j; }>);
> > +  check(std::cw<[](auto i, auto j){ return i + j; }>);
> > +}
> > +
> > +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>);
> > +}
> > +
> > +struct Indexable1
> > +{
> > +  constexpr int
> > +  operator[](int i, int j) const noexcept
> > +  { return i*j; }
> > +};
> > +
> > +template<typename Obj, typename... Args>
> > +  concept indexable = requires (Obj obj, Args... args)
> > +  {
> > +    obj[args...];
> > +  };
> > +
> > +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>>);
> > +
> > +  static_assert(indexable<decltype(cind), decltype(ci), decltype(cj)>);
> > +  static_assert(!indexable<decltype(cind), int, decltype(cj)>);
> > +  static_assert(!indexable<decltype(cind), int, int>);
> > +}
> > +
> > +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>>);
> > +
> > +  static_assert(indexable<decltype(cind), decltype(ci), decltype(cj)>);
> > +  static_assert(!indexable<decltype(cind), int, decltype(cj)>);
> > +  static_assert(!indexable<decltype(cind), int, int>);
> > +}
> > +
> > +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>;
> > +
> > +  check_same(cind[], std::cw<1>);
> > +  check_same(cind[ci], std::cw<2>);
> > +  check_same(cind[ci, cj], std::cw<2*3>);
> > +}
> > +
> > +struct Divide
> > +{
> > +  int value;
> > +
> > +  constexpr int
> > +  divide(int div) const
> > +  { return value / div; }
> > +
> > +};
> > +
> > +constexpr void
> > +test_member_pointer()
> > +{
> > +  constexpr int nom = 42;
> > +  constexpr int denom = 3;
> > +
> > +  auto cdiv = std::cw<&Divide::divide>;
> > +  auto co = std::cw<Divide{nom}>;
> > +
> > +  auto expect_unwrapped = nom / denom;
> > +  check_same(((&co)->*(&Divide::divide))(denom), expect_unwrapped);
> > +  check_same((&(co.value)->*cdiv)(denom), expect_unwrapped);
> > +  check_same(((&decltype(co)::value)->*cdiv)(denom), expect_unwrapped);
> > +}
> > +
> > +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; }
> > +};
> > +
> > +template<typename Lhs, typename Rhs>
> > +  concept has_op_and = requires (Lhs lhs, Rhs rhs)
> > +  {
> > +    lhs && rhs;
> > +  };
> > +
> > +constexpr void
> > +test_logic()
> > +{
> > +  auto ctrue = std::cw<true>;
> > +  auto cfalse = std::cw<false>;
> > +  auto truthy = Truthy{};
> > +
> > +  auto check_and = [](auto lhs, auto rhs)
> > +  {
> > +    static_assert(lhs && rhs);
> > +    static_assert(std::same_as<decltype(lhs && rhs), bool>);
> > +  };
> > +
> > +  auto check_or = [](auto lhs, auto rhs)
> > +  {
> > +    static_assert(lhs || rhs);
> > +    static_assert(std::same_as<decltype(lhs || rhs), bool>);
> > +  };
> > +
> > +  check_and(ctrue, ctrue);
> > +  check_or(ctrue, cfalse);
> > +  check_and((std::cw<0> < std::cw<1>), (std::cw<1> < std::cw<5>));
> > +  check_or((std::cw<0> < std::cw<1>), (std::cw<1> < std::cw<5>));
> > +
> > +  auto ctruthy = std::cw<Truthy{}>;
> > +  static_assert(has_op_and<decltype(truthy), bool>);
> > +  static_assert(!has_op_and<decltype(ctruthy), decltype(ctrue)>);
> > +  static_assert(!has_op_and<decltype(ctruthy), bool>);
> > +}
> > +
> > +struct ThreeWayComp
> > +{
> > +  friend
> > +  constexpr std::strong_ordering
> > +  operator<=>(ThreeWayComp lhs, ThreeWayComp rhs)
> > +  { return lhs.value <=> rhs.value; }
> > +
> > +  int value;
> > +};
> > +
> > +constexpr void
> > +test_three_way()
> > +{
> > +  auto ctrue = std::cw<true>;
> > +  auto cfalse = std::cw<false>;
> > +
> > +  check_same(std::cw<ThreeWayComp{0}> < std::cw<ThreeWayComp{1}>,
> ctrue);
> > +  check_same(std::cw<ThreeWayComp{2}> > std::cw<ThreeWayComp{1}>,
> ctrue);
> > +  check_same(std::cw<ThreeWayComp{2}> >= std::cw<ThreeWayComp{1}>,
> ctrue);
> > +  check_same(std::cw<ThreeWayComp{0}> <= std::cw<ThreeWayComp{1}>,
> ctrue);
> > +  check_same(std::cw<ThreeWayComp{0}> >= std::cw<ThreeWayComp{1}>,
> cfalse);
> > +
> > +  check_same(std::cw<ThreeWayComp{0}> < ThreeWayComp{1}, true);
> > +  check_same(ThreeWayComp{2} > std::cw<ThreeWayComp{1}>, true);
> > +}
> > +
> > +struct EqualityComp
> > +{
> > +  friend
> > +  constexpr bool
> > +  operator==(EqualityComp lhs, EqualityComp rhs)
> > +  { return lhs.value == rhs.value; }
> > +
> > +  int value;
> > +};
> > +
> > +constexpr void
> > +test_equality()
> > +{
> > +  auto ctrue = std::cw<true>;
> > +  check_same(std::cw<EqualityComp{1}> == std::cw<EqualityComp{1}>,
> ctrue);
> > +  check_same(std::cw<EqualityComp{0}> != std::cw<EqualityComp{1}>,
> ctrue);
> > +
> > +  check_same(std::cw<EqualityComp{1}> == EqualityComp{1}, true);
> > +  check_same(EqualityComp{0} != std::cw<EqualityComp{1}>, true);
> > +}
> > +
> > +struct ConstAssignable
> > +{
> > +  int value;
> > +
> > +  constexpr ConstAssignable
> > +  operator=(int rhs) const
> > +  { return ConstAssignable{rhs}; }
> > +
> > +  friend constexpr bool
> > +  operator==(const ConstAssignable& lhs, const ConstAssignable& rhs)
> > +  { return lhs.value == rhs.value; }
> > +};
> > +
> > +constexpr void
> > +test_assignment()
> > +{
> > +  check_same(std::cw<ConstAssignable{3}> = std::cw<2>,
> > +          std::cw<ConstAssignable{2}>);
> > +}
> > +
> > +
> > +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
> > +{
>
> With the new adl.cc, can we throw all of these out? "These"
> refers to everyting in and everything using namespace
> member_ops.
>
I do not mind keeping them, I like that we test that every operator
is defined in a way to support both.



> > +  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/other_wrappers.cc
> b/libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc
> > new file mode 100644
> > index 00000000000..3c3cfafd6ed
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc
> > @@ -0,0 +1,75 @@
> > +// { dg-do run { target c++26 } }
> > +#include <type_traits>
> > +#include <concepts>
> > +
> > +#include <testsuite_hooks.h>
> > +
> > +template<typename Type, Type Value>
> > +  struct ConstWrapper
> > +  {
> > +    constexpr static Type value = Value;
> > +  };
> > +
> > +constexpr void
> > +check_same(auto actual, auto expected)
> > +{
> > +  VERIFY(actual == expected);
> > +  static_assert(std::same_as<decltype(actual), decltype(expected)>);
> > +}
> > +
> > +constexpr void
> > +test_mix_integer_constant()
> > +{
> > +  auto i4 = std::integral_constant<int, 4>{};
> > +  auto c3 = std::cw<3>;
> > +  auto w2 = ConstWrapper<int, 2>{};
> > +
> > +  check_same(i4 + c3, std::cw<7>);
> > +  check_same(c3 + i4, std::cw<7>);
> > +  check_same(c3 + w2, std::cw<5>);
> > +  check_same(w2 + c3, std::cw<5>);
> > +}
> > +
> > +constexpr void
> > +test_array()
> > +{
> > +  constexpr double x[] = {1.1, 2.2, 3.3};
> > +  auto cx = std::cw<x>;
> > +  auto i2 = std::integral_constant<int, 2>{};
> > +  auto w2 = ConstWrapper<int, 2>{};
> > +
> > +  check_same(x[i2], x[2]);
> > +  check_same(cx[i2], std::cw<x[2]>);
> > +  check_same(cx[w2], std::cw<x[2]>);
> > +}
> > +
> > +constexpr void
> > +test_function_object()
> > +{
> > +  auto cadd = std::cw<[](int i, int j) { return i + j; }>;
> > +  auto i4 = std::integral_constant<int, 4>{};
> > +  auto c3 = std::cw<3>;
> > +  auto w2 = ConstWrapper<int, 2>{};
> > +
> > +  check_same(cadd(i4, c3), std::cw<7>);
> > +  check_same(cadd(c3, i4), std::cw<7>);
> > +  check_same(cadd(w2, c3), std::cw<5>);
> > +  check_same(cadd(c3, w2), std::cw<5>);
> > +}
> > +
> > +constexpr bool
> > +test_all()
> > +{
> > +  test_mix_integer_constant();
> > +  test_array();
> > +  test_function_object();
> > +  return true;
> > +}
> > +
> > +int
> > +main()
> > +{
> > +  test_all();
> > +  static_assert(test_all());
> > +  return 0;
> > +}
> > 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
>
>

Reply via email to