On Thu, Sep 4, 2025 at 2:20 PM Luc Grosheintz <luc.groshei...@gmail.com> 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> > LGTM, I have a suggestion for one additional test that I have already added, and will post revision with it. > --- > 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; > I am missing a test for data member pointers, as I am coauthor, I will just one add one. > + > + 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 > +{ > + 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 > -- > 2.51.0 > >