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