On Tue, Sep 2, 2025 at 6:22 PM Luc Grosheintz <[email protected]> wrote:
> > > On 9/2/25 13:45, Tomasz Kaminski wrote: > > On Tue, Sep 2, 2025 at 10:07 AM Luc Grosheintz <[email protected] > > > > wrote: > > > >> This is a partial implementation of P2781R9. It adds std::cw and > >> std::constant_wrapper, but doesn't modify __integral_constant_like for > >> span/mdspan. > >> > >> libstdc++-v3/ChangeLog: > >> > >> * include/bits/version.def (constant_wrapper): Add. > >> * include/bits/version.h: Regenerate. > >> * include/std/type_traits (_CwFixedValue): New class. > >> (_IndexSequence): New struct. > >> (_BuildIndexSequence): New struct. > >> (_ConstExprParam): New concept. > >> (_CwOperators): New struct. > >> (constant_wrapper): New struct. > >> (cw): New global constant. > >> * src/c++23/std.cc.in (constant_wrapper): Add. > >> (cw): Add. > >> * testsuite/20_util/constant_wrapper/adl.cc: New test. > >> * testsuite/20_util/constant_wrapper/ex.cc: New test. > >> * testsuite/20_util/constant_wrapper/generic.cc: New test. > >> * testsuite/20_util/constant_wrapper/instantiate.cc: New test. > >> * testsuite/20_util/constant_wrapper/op_comma_neg.cc: New test. > >> * testsuite/20_util/constant_wrapper/version.cc: New test. > >> > >> Signed-off-by: Luc Grosheintz <[email protected]> > >> > > Thanks, the implementation looks really solid. I have included decent > > amount of suggestion > > for the test, but this is mostly additional test cases. > > > > Generally, I would like to see additional test file, that would test with > > other constant-wrapper like, > > like integral_constant, and one defined in the test. Testing a few > > operators should be fine. > > > > For binary operators, we should also check that adding runtime value > (like > > integer) produces > > runtime result, and not constant_wrapper. Similarly a small separate test > > should be fine. > > > > Finally, the assignment operators may work at runtime, if we have: > > struct ConstAssignable // tuple<int&> > > { > > ConstAssignable const& operator=(int) const > > { return *this; } > > }; > > > > cw<ConstAssignable> = 10; // would also compile and produce > ConstAssignable. > > > > Thank you. That clarified a lot of my questions. Can I delete adl.cc in > v3? The reason is that instantiate.cc has mutated to a much stronger ADL > test. > > I replied to one comment. Everything else is clear and will be updated. > > > > > --- > >> libstdc++-v3/include/bits/version.def | 8 + > >> libstdc++-v3/include/bits/version.h | 10 + > >> libstdc++-v3/include/std/type_traits | 371 +++++++++++ > >> libstdc++-v3/src/c++23/std.cc.in | 4 + > >> .../testsuite/20_util/constant_wrapper/adl.cc | 42 ++ > >> .../testsuite/20_util/constant_wrapper/ex.cc | 45 ++ > >> .../20_util/constant_wrapper/generic.cc | 252 ++++++++ > >> .../20_util/constant_wrapper/instantiate.cc | 575 ++++++++++++++++++ > >> .../20_util/constant_wrapper/op_comma_neg.cc | 14 + > >> .../20_util/constant_wrapper/version.cc | 11 + > >> 10 files changed, 1332 insertions(+) > >> create mode 100644 > libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc > >> create mode 100644 > libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc > >> create mode 100644 > >> libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > >> create mode 100644 > >> libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc > >> create mode 100644 > >> libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc > >> create mode 100644 > >> libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc > >> > >> diff --git a/libstdc++-v3/include/bits/version.def > >> b/libstdc++-v3/include/bits/version.def > >> index 84c755da10e..4f8d50bca30 100644 > >> --- a/libstdc++-v3/include/bits/version.def > >> +++ b/libstdc++-v3/include/bits/version.def > >> @@ -393,6 +393,14 @@ ftms = { > >> }; > >> }; > >> > >> +ftms = { > >> + name = constant_wrapper; > >> + values = { > >> + v = 202506; > >> + cxxmin = 26; > >> + }; > >> +}; > >> + > >> ftms = { > >> name = has_unique_object_representations; > >> values = { > >> diff --git a/libstdc++-v3/include/bits/version.h > >> b/libstdc++-v3/include/bits/version.h > >> index 410e3205339..2403584e57a 100644 > >> --- a/libstdc++-v3/include/bits/version.h > >> +++ b/libstdc++-v3/include/bits/version.h > >> @@ -430,6 +430,16 @@ > >> #endif /* !defined(__cpp_lib_byte) && defined(__glibcxx_want_byte) */ > >> #undef __glibcxx_want_byte > >> > >> +#if !defined(__cpp_lib_constant_wrapper) > >> +# if (__cplusplus > 202302L) > >> +# define __glibcxx_constant_wrapper 202506L > >> +# if defined(__glibcxx_want_all) || > >> defined(__glibcxx_want_constant_wrapper) > >> +# define __cpp_lib_constant_wrapper 202506L > >> +# endif > >> +# endif > >> +#endif /* !defined(__cpp_lib_constant_wrapper) && > >> defined(__glibcxx_want_constant_wrapper) */ > >> +#undef __glibcxx_want_constant_wrapper > >> + > >> #if !defined(__cpp_lib_has_unique_object_representations) > >> # if (__cplusplus >= 201703L) && > >> (defined(_GLIBCXX_HAVE_BUILTIN_HAS_UNIQ_OBJ_REP)) > >> # define __glibcxx_has_unique_object_representations 201606L > >> diff --git a/libstdc++-v3/include/std/type_traits > >> b/libstdc++-v3/include/std/type_traits > >> index 4636457eb5a..26cbbb4fd5b 100644 > >> --- a/libstdc++-v3/include/std/type_traits > >> +++ b/libstdc++-v3/include/std/type_traits > >> @@ -41,6 +41,7 @@ > >> > >> #define __glibcxx_want_bool_constant > >> #define __glibcxx_want_bounded_array_traits > >> +#define __glibcxx_want_constant_wrapper > >> #define __glibcxx_want_has_unique_object_representations > >> #define __glibcxx_want_integral_constant_callable > >> #define __glibcxx_want_is_aggregate > >> @@ -4302,6 +4303,376 @@ template<typename _Ret, typename _Fn, > typename... > >> _Args> > >> }; > >> #endif // C++11 > >> > >> +#ifdef __cpp_lib_constant_wrapper // C++ >= 26 > > > > I wonder if we should put it into a separate file. This would be useful > if > > we ever separate > > type traits, and it does not seem to have any dependencies. Like > > bits/constant_wrapper.h. > > It will be also used by submdspan later. > > > > Let me clarify: by "I wonder ..." do you mean: > > - some time in the future, it would be nicer if this where in a > different file. Similar to Jonathans comment on PATCH 1/3; > > - or please move these classes to <bits/constant_wrapper.h>? > Ah, sorry my mind got fixated on constant_wrapper being in <utility>, and not type traits, where the move would make sense, > > If it's the former the rest of this block of text is irrelevant. If > it's the latter: I see that you've merged the preparatory commit > that moves the two helpers needed to implement constant_wrapper. > To move the constant_wrapper related code to <bits/constant_wrapper.h> > we need to also move the two helper classes _Index_tuple and > _Build_index_tuple. Moving them to <bits/constant_wrapper.h> feels > like creating about as much mess as we are cleaning up. Moving them to > <bits/index_tuple.h> would run counter to the idea of preventing lots > of very small files (which as Jonathan puts it "would be silly"). > Additionally, I don't think we can benefit from splitting the code > into a separate header, because: a) we need to include it back into > <type_traits> (the standard demands it); and b) we include > <type_traits> from <array> which we include from <span> (which we > include from <mdspan>). Given the contents of <type_traits> (true_type, > is_*, enable_if, etc) I'm skeptical we can quickly break the > dependency. > > TL;DR I'd like to suggest leaving it here for now, because we can't > capitalize on moving the code. > I agree. > > > > > > > + template<typename _Tp> > >> + struct _CwFixedValue > >> + { > >> + using _S_type = _Tp; > >> + > >> + constexpr > >> + _CwFixedValue(_S_type __v) noexcept > >> + : _M_data(__v) { } > >> + > >> + _S_type _M_data; > >> + }; > >> + > >> + template<typename _Tp, size_t _Extent> > >> + struct _CwFixedValue<_Tp[_Extent]> > >> + { > >> + using _S_type = _Tp[_Extent]; > >> + > >> + constexpr > >> + _CwFixedValue(_Tp (&__arr)[_Extent]) noexcept > >> + : _CwFixedValue(__arr, typename > >> _Build_index_tuple<_Extent>::__type()) > >> + { } > >> + > >> + template<size_t... _Indices> > >> + constexpr > >> + _CwFixedValue(_Tp (&__arr)[_Extent], _Index_tuple<_Indices...>) > >> noexcept > >> + : _M_data{__arr[_Indices]...} > >> + { } > >> + > >> + _Tp _M_data[_Extent]; > >> + }; > >> + > >> + template<typename _Tp, size_t _Extent> > >> + _CwFixedValue(_Tp (&)[_Extent]) -> _CwFixedValue<_Tp[_Extent]>; > >> + > >> + template<_CwFixedValue _Tp, > >> + typename = typename decltype(_CwFixedValue(_Tp))::_S_type> > >> + struct constant_wrapper; > >> + > >> + template<typename _Tp> > >> + concept _ConstExprParam = requires > >> + { > >> + typename constant_wrapper<_Tp::value>; > >> + }; > >> + > >> + struct _CwOperators > >> + { > >> + template<_ConstExprParam _Tp> > >> + friend constexpr auto > >> + operator+(_Tp) noexcept -> constant_wrapper<(+_Tp::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Tp> > >> + friend constexpr auto > >> + operator-(_Tp) noexcept -> constant_wrapper<(-_Tp::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Tp> > >> + friend constexpr auto > >> + operator~(_Tp) noexcept -> constant_wrapper<(~_Tp::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Tp> > >> + friend constexpr auto > >> + operator!(_Tp) noexcept -> constant_wrapper<(!_Tp::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Tp> > >> + friend constexpr auto > >> + operator&(_Tp) noexcept -> constant_wrapper<(&_Tp::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Tp> > >> + friend constexpr auto > >> + operator*(_Tp) noexcept -> constant_wrapper<(*_Tp::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator+(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value + _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator-(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value - _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator*(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value * _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator/(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value / _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator%(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value % _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator<<(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value << _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator>>(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value >> _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator&(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value & _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator|(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value | _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator^(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value ^ _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + requires (!is_constructible_v<bool, decltype(_Left::value)> > >> + || !is_constructible_v<bool, decltype(_Right::value)>) > >> + friend constexpr auto > >> + operator&&(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value && _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + requires (!is_constructible_v<bool, decltype(_Left::value)> > >> + || !is_constructible_v<bool, decltype(_Right::value)>) > >> + friend constexpr auto > >> + operator||(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value || _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator<=>(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value <=> _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator<(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value < _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator<=(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value <= _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator==(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value == _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator!=(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value != _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator>(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value > _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator>=(_Left, _Right) noexcept > >> + -> constant_wrapper<(_Left::value >= _Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator,(_Left, _Right) noexcept = delete; > >> + > >> + template<_ConstExprParam _Left, _ConstExprParam _Right> > >> + friend constexpr auto > >> + operator->*(_Left, _Right) noexcept > >> + -> constant_wrapper<_Left::value->*(_Right::value)> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam... _Args> > >> + constexpr auto > >> + operator()(this _Tp, _Args...) noexcept > >> + requires > >> + requires(_Args...) { > >> constant_wrapper<_Tp::value(_Args::value...)>(); } > >> + { return constant_wrapper<_Tp::value(_Args::value...)>{}; } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam... _Args> > >> + constexpr auto > >> + operator[](this _Tp, _Args...) noexcept > >> + -> constant_wrapper<(_Tp::value[_Args::value...])> > >> + { return {}; } > >> + > >> + template<_ConstExprParam _Tp> > >> + constexpr auto > >> + operator++(this _Tp) noexcept > >> + requires requires(_Tp::value_type __x) { ++__x; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return ++__x; }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp> > >> + constexpr auto > >> + operator++(this _Tp, int) noexcept > >> + requires requires(_Tp::value_type __x) { __x++; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x++; }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp> > >> + constexpr auto > >> + operator--(this _Tp) noexcept > >> + requires requires(_Tp::value_type __x) { --__x; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return --__x; }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp> > >> + constexpr auto > >> + operator--(this _Tp, int) noexcept > >> + requires requires(_Tp::value_type __x) { __x--; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x--; }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator+=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x += _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x += _Right::value; > }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator-=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x -= _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x -= _Right::value; > }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator*=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x *= _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x *= _Right::value; > }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator/=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x /= _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x /= _Right::value; > }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator%=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x %= _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x %= _Right::value; > }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator&=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x &= _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x &= _Right::value; > }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator|=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x |= _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x |= _Right::value; > }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator^=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x ^= _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x ^= _Right::value; > }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator<<=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x <<= _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x <<= _Right::value; > }()>{}; > >> + } > >> + > >> + template<_ConstExprParam _Tp, _ConstExprParam _Right> > >> + constexpr auto > >> + operator>>=(this _Tp, _Right) noexcept > >> + requires requires(_Tp::value_type __x) { __x >>= _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = _Tp::value; return __x >>= _Right::value; > }()>{}; > >> + } > >> + }; > >> + > >> + template<_CwFixedValue _X, typename> > >> + struct constant_wrapper : _CwOperators > >> + { > >> + static constexpr const auto& value = _X._M_data; > >> + using type = constant_wrapper; > >> + using value_type = typename decltype(_X)::_S_type; > >> + > >> + template<_ConstExprParam _Right> > >> + constexpr auto > >> + operator=(_Right) const noexcept > >> + requires requires(value_type __x) { __x = _Right::value; } > >> + { > >> + return constant_wrapper< > >> + [] { auto __x = value; return __x = _Right::value; }()>{}; > >> + } > >> + > >> + constexpr > >> + operator decltype(auto)() const noexcept > >> + { return value; } > >> + }; > >> + > >> + template<_CwFixedValue _Tp> > >> + constexpr auto cw = constant_wrapper<_Tp>{}; > >> +#endif > >> + > >> /// @} group metaprogramming > >> > >> _GLIBCXX_END_NAMESPACE_VERSION > >> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/ > >> std.cc.in > >> index 4888b8b4f23..a217a87330b 100644 > >> --- a/libstdc++-v3/src/c++23/std.cc.in > >> +++ b/libstdc++-v3/src/c++23/std.cc.in > >> @@ -2997,6 +2997,10 @@ export namespace std > >> using std::conditional_t; > >> using std::conjunction; > >> using std::conjunction_v; > >> +#if __cpp_lib_constant_wrapper > >> + using std::constant_wrapper; > >> + using std::cw; > >> +#endif > >> using std::decay; > >> using std::decay_t; > >> using std::disjunction; > >> diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc > >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc > >> new file mode 100644 > >> index 00000000000..7ee754fe4b1 > >> --- /dev/null > >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc > >> @@ -0,0 +1,42 @@ > >> +// { dg-do run { target c++26 } } > >> +#include <type_traits> > >> + > >> +#include <testsuite_hooks.h> > >> + > >> +namespace adl > >> +{ > >> + struct Addable > >> + { > >> + double x; > >> + > >> + friend constexpr Addable > >> + operator+(Addable lhs, Addable rhs) > >> + { return Addable{lhs.x + rhs.x}; } > >> + > >> + friend constexpr bool > >> + operator==(Addable lhs, Addable rhs) > >> + { return lhs.x == rhs.x; } > >> + }; > >> +} > >> + > >> +constexpr void > >> +test_addable() > >> +{ > >> + auto check = [](auto a, auto b) > >> + { > >> + if constexpr (a + b == adl::Addable{5.0}) > >> + return true; > >> + else > >> + return false; > >> + }; > >> + > >> + constexpr adl::Addable a{2.0}, b{3.0}; > >> + VERIFY(check(std::cw<a>, std::cw<b>)); > >> +} > >> + > >> +int > >> +main() > >> +{ > >> + test_addable(); > >> + return 0; > >> +} > >> diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc > >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc > >> new file mode 100644 > >> index 00000000000..f46af929030 > >> --- /dev/null > >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc > >> @@ -0,0 +1,45 @@ > >> +// { dg-do run { target c++26 } } > >> > > This could be changed to dg-do compile, as we do not really care about > > runtime value, > > just final_phase compiling. > > > >> +#include <type_traits> > >> + > >> +#include <testsuite_hooks.h> > >> + > >> +constexpr auto > >> +initial_phase(auto quantity_1, auto quantity_2) > >> +{ return quantity_1 + quantity_2; } > >> + > >> +constexpr auto > >> +middle_phase(auto tbd) > >> +{ return tbd; } > >> + > >> +constexpr bool > >> +final_phase(auto gathered, auto available) > >> +{ > >> + if constexpr (gathered == available) > >> + return true; > >> + else > >> + return false; > >> +} > >> + > >> +void > >> +impeccable_underground_planning() > >> +{ > >> + auto gathered_quantity = middle_phase(initial_phase(std::cw<42>, > >> std::cw<13>)); > >> + static_assert(gathered_quantity == 55); > >> + auto all_available = std::cw<55>; > >> + VERIFY(final_phase(gathered_quantity, all_available)); > >> +} > >> + > >> +// void > >> +// deeply_flawed_underground_planning() > >> > > Then we could mark that we expect error from here. > > > >> +// { > >> +// constexpr auto gathered_quantity = middle_phase(initial_phase(42, > >> 13)); > >> +// constexpr auto all_available = 55; > >> +// final_phase(gathered_quantity, all_available); > >> +// } > >> + > >> +int > >> +main() > >> +{ > >> + impeccable_underground_planning(); > >> + return 0; > >> +} > >> diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > >> new file mode 100644 > >> index 00000000000..a74ce0b1d8b > >> --- /dev/null > >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > >> @@ -0,0 +1,252 @@ > >> +// { dg-do run { target c++26 } } > >> +#include <type_traits> > >> +#include <utility> > >> + > >> +#include <testsuite_hooks.h> > >> + > >> +constexpr void > >> +test_c_arrays() > >> > > Could you add a test for string-literals, their support was a major > reason > > for having all the complications. > > > >> +{ > >> + constexpr double x[] = {1.1, 2.2, 3.3}; > >> + auto access = [](auto x, size_t i) > >> + { return x[i]; }; > >> + > >> + VERIFY(access(std::cw<x>, 0) == x[0]); > >> + VERIFY(access(std::cw<x>, 1) == x[1]); > >> + VERIFY(access(std::cw<x>, 2) == x[2]); > >> +} > >> + > >> +constexpr void > >> +test_ints() > >> +{ > >> + std::constant_wrapper<2> two; > >> + std::constant_wrapper<3> three; > >> + std::constant_wrapper<5> five; > >> + > >> + VERIFY(two + 3 == 5); > >> + static_assert(std::same_as<decltype(two + 3), int>); > >> + > >> + VERIFY(two + three == 5); > >> + VERIFY(two + three == five); > >> + static_assert(std::same_as<decltype(two + three), > >> std::constant_wrapper<5>>); > >> + > >> + VERIFY(two == std::cw<2>); > >> + VERIFY(two + 3 == std::cw<5>); > >> +} > >> + > >> +constexpr int > >> +add(int i, int j) > >> +{ return i + j; } > >> + > >> +struct Add > >> +{ > >> + constexpr int > >> + operator()(int i, int j) const noexcept > >> + { return i + j; } > >> +}; > >> + > >> +constexpr void > >> +test_function_object() > >> +{ > >> + auto cadd = std::cw<Add{}>; > >> + auto ci = std::cw<2>; > >> + auto cj = std::cw<3>; > >> + > >> + VERIFY(cadd(ci, cj) == 5); > >> + static_assert(std::same_as<decltype(cadd(ci, cj)), > >> std::constant_wrapper<5>>); > >> + > >> + // Invalid: > >> + // VERIFY(cadd(2, cj) == 5); > >> + // VERIFY(cadd(2, 3) == 5); > >> > > Please add negative tests for this, they can be done by static asserting > > that cadd is not invokable. > > > >> +} > >> + > >> +constexpr void > >> +test_function_pointer() > >> > > +{ > >> + auto cptr = std::cw<add>; > >> + auto ci = std::cw<2>; > >> + auto cj = std::cw<3>; > >> + > >> + VERIFY(cptr(ci, cj) == 5); > >> + static_assert(std::same_as<decltype(cptr(ci, cj)), > >> std::constant_wrapper<5>>); > >> + > >> + VERIFY(cptr(2, cj) == 5); > >> + static_assert(std::same_as<decltype(cptr(2, cj)), int>); > >> + > >> + VERIFY(cptr(2, 3) == 5); > >> + static_assert(std::same_as<decltype(cptr(2, 3)), int>); > >> > > Similar here, test that operator[] does not work with runtime values. > > > >> +} > >> + > >> +struct Indexable1 > >> +{ > >> + constexpr int > >> + operator[](int i, int j) const noexcept > >> + { return i*j; } > >> +}; > >> + > >> +constexpr void > >> +test_indexable1() > >> +{ > >> + auto cind = std::cw<Indexable1{}>; > >> + auto ci = std::cw<2>; > >> + auto cj = std::cw<3>; > >> + VERIFY(cind[ci, cj] == ci*cj); > >> + static_assert(std::same_as<decltype(cind[ci, cj]), > >> std::constant_wrapper<6>>); > >> +} > >> + > >> +struct Indexable2 > >> +{ > >> + template<typename I, typename J> > >> + constexpr int > >> + operator[](I i, J j) const noexcept > >> + { return i*j; } > >> +}; > >> + > >> +constexpr void > >> +test_indexable2() > >> +{ > >> + auto cind = std::cw<Indexable2{}>; > >> + auto ci = std::cw<2>; > >> + auto cj = std::cw<3>; > >> + VERIFY(cind[ci, cj] == ci*cj); > >> + static_assert(std::same_as<decltype(cind[ci, cj]), > >> std::constant_wrapper<6>>); > >> +} > >> + > >> +struct Indexable3 > >> +{ > >> + template<typename... Is> > >> + constexpr int > >> + operator[](Is... i) const noexcept > >> + { return (1 * ... * i); } > >> +}; > >> + > >> +constexpr void > >> +test_indexable3() > >> +{ > >> + auto cind = std::cw<Indexable3{}>; > >> + auto ci = std::cw<2>; > >> + auto cj = std::cw<3>; > >> + VERIFY(cind[] == 1); > >> + static_assert(std::same_as<decltype(cind[]), > std::constant_wrapper<1>>); > >> + VERIFY(cind[ci] == ci); > >> + static_assert(std::same_as<decltype(cind[ci]), > >> std::constant_wrapper<2>>); > >> + VERIFY(cind[ci, cj] == ci*cj); > >> + static_assert(std::same_as<decltype(cind[ci, cj]), > >> std::constant_wrapper<6>>); > >> +} > >> + > >> +struct Divide > >> +{ > >> + int value; > >> + > >> + constexpr int > >> + divide(int div) const > >> + { return value / div; } > >> + > >> +}; > >> + > >> +constexpr void > >> +test_member_pointer() > >> +{ > >> + auto cdiv = std::cw<&Divide::divide>; > >> + auto co = std::cw<Divide{42}>; > >> + VERIFY(((&co)->* cdiv)(3) == co.value.value / 3); > >> > > I would prefer if we would also use static_assert to check that > > constant_wrapper is produced. > > I think the following should also work and produce int. > > (&co)->*(&Divide::divide) > > Similary: > > &co::value->cdiv > > > >> +} > >> + > >> +constexpr void > >> +test_pseudo_mutator() > >> +{ > >> + auto ci = std::cw<3>; > >> + auto cmmi = --ci; > >> + VERIFY(ci.value == 3); > >> + VERIFY(cmmi.value == 2); > >> + > >> + auto cimm = ci--; > >> + VERIFY(ci.value == 3); > >> + VERIFY(cimm.value == 3); > >> +} > >> + > >> +struct Truthy > >> +{ > >> + constexpr operator bool() const > >> + { return true; } > >> +}; > >> + > >> +constexpr void > >> +test_logic() > >> +{ > >> + auto ctrue = std::cw<true>; > >> + auto cfalse = std::cw<false>; > >> + auto truthy = Truthy{}; > >> + > >> + VERIFY(ctrue && ctrue); > >> > > Please also add checks for the type of expression, the above should be > > constant > > wrapper. The remaining to not. > > > >> + VERIFY(ctrue || cfalse); > >> + VERIFY(truthy && true); > >> + VERIFY((std::cw<0> < std::cw<1>) && (std::cw<1> < std::cw<5>)); > >> + > >> + // auto ctruthy = std::cw<Truthy{}>; > >> > > It would be good to put them in a negative test, as we test for specific > > constraint. > > I mean static_assert on concept should be fine. > > > >> + // Invalid: > >> + // VERIFY(ctrue && ctruthy); > >> + // VERIFY(true && ctruthy); > >> > > +} > >> + > >> +struct ThreeWayComp > >> +{ > >> + friend > >> + constexpr std::strong_ordering > >> + operator<=>(ThreeWayComp lhs, ThreeWayComp rhs) > >> + { return lhs.value <=> rhs.value; } > >> + > >> + int value; > >> +}; > >> + > >> +constexpr void > >> +test_three_way() > >> +{ > >> + VERIFY(std::cw<ThreeWayComp{0}> < std::cw<ThreeWayComp{1}>); > >> + VERIFY(std::cw<ThreeWayComp{2}> > std::cw<ThreeWayComp{1}>); > >> + VERIFY(std::cw<ThreeWayComp{2}> >= std::cw<ThreeWayComp{1}>); > >> + VERIFY(std::cw<ThreeWayComp{0}> <= std::cw<ThreeWayComp{1}>); > >> > > Test also with runtime values. > > > >> +} > >> + > >> +struct EqualityComp > >> +{ > >> + friend > >> + constexpr bool > >> + operator==(EqualityComp lhs, EqualityComp rhs) > >> + { return lhs.value == rhs.value; } > >> + > >> + int value; > >> +}; > >> + > >> +constexpr void > >> +test_equality() > >> +{ > >> + VERIFY(std::cw<EqualityComp{1}> == std::cw<EqualityComp{1}>); > >> + VERIFY(std::cw<EqualityComp{0}> != std::cw<EqualityComp{1}>); > >> > > Similar here, test comparing against runtime values. > > > >> +} > >> + > >> +constexpr bool > >> +test_all() > >> +{ > >> + test_c_arrays(); > >> + test_ints(); > >> + test_function_object(); > >> + test_function_pointer(); > >> + test_indexable1(); > >> + test_indexable2(); > >> + test_indexable3(); > >> + test_member_pointer(); > >> + test_pseudo_mutator(); > >> + test_logic(); > >> + test_three_way(); > >> + test_equality(); > >> + return true; > >> +} > >> + > >> +int > >> +main() > >> +{ > >> + test_all(); > >> + static_assert(test_all()); > >> + return 0; > >> +} > >> diff --git > >> a/libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc > >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc > >> new file mode 100644 > >> index 00000000000..4f1232598d6 > >> --- /dev/null > >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc > >> @@ -0,0 +1,575 @@ > >> +// { dg-do run { target c++26 } } > >> +#include <type_traits> > >> +#include <utility> > >> + > >> +#include <testsuite_hooks.h> > >> + > >> +namespace free_ops > >> +{ > >> + template<int OpId> > >> + struct UnaryOps > >> + { > >> + friend constexpr int > >> + operator+(UnaryOps) noexcept requires (OpId == 0) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator-(UnaryOps) noexcept requires (OpId == 1) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator~(UnaryOps) noexcept requires (OpId == 2) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator!(UnaryOps) noexcept requires (OpId == 3) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator&(UnaryOps) noexcept requires (OpId == 4) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator*(UnaryOps) noexcept requires (OpId == 5) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator++(UnaryOps) noexcept requires (OpId == 6) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator++(UnaryOps, int) noexcept requires (OpId == 7) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator--(UnaryOps) noexcept requires (OpId == 8) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator--(UnaryOps, int) noexcept requires (OpId == 9) > >> + { return OpId; } > >> + }; > >> +} > >> + > >> +namespace member_ops > >> +{ > >> + template<int OpId> > >> + struct UnaryOps > >> + { > >> + constexpr int > >> + operator+() const noexcept requires (OpId == 0) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator-() const noexcept requires (OpId == 1) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator~() const noexcept requires (OpId == 2) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator!() const noexcept requires (OpId == 3) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator&() const noexcept requires (OpId == 4) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator*() const noexcept requires (OpId == 5) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator++() const noexcept requires (OpId == 6) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator++(int) const noexcept requires (OpId == 7) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator--() const noexcept requires (OpId == 8) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator--(int) const noexcept requires (OpId == 9) > >> + { return OpId; } > >> + }; > >> +} > >> + > >> +constexpr size_t n_unary_ops = 10; > >> + > >> +template<template<int> typename Ops, int OpId> > >> + constexpr void > >> + test_unary_operator() > >> + { > >> + auto x = std::cw<Ops<OpId>{}>; > >> + > >> + auto check = [](auto c) > >> + { > >> + VERIFY(c == OpId); > >> + static_assert(std::same_as<decltype(c), > >> std::constant_wrapper<OpId>>); > >> + }; > >> + > >> + if constexpr (OpId == 0) > >> + check(+x); > >> + if constexpr (OpId == 1) > >> + check(-x); > >> + if constexpr (OpId == 2) > >> + check(~x); > >> + if constexpr (OpId == 3) > >> + check(!x); > >> + if constexpr (OpId == 4) > >> + check(&x); > >> + if constexpr (OpId == 5) > >> + check(*x); > >> + if constexpr (OpId == 6) > >> + check(++x); > >> + if constexpr (OpId == 7) > >> + check(x++); > >> + if constexpr (OpId == 8) > >> + check(--x); > >> + if constexpr (OpId == 9) > >> + check(x--); > >> + > >> + static_assert(n_unary_ops == 10); > >> + } > >> + > >> +constexpr void > >> +test_unary_operators_all() > >> +{ > >> + auto run = []<size_t... Idx>(std::integer_sequence<size_t, Idx...>) > >> + { > >> + (test_unary_operator<free_ops::UnaryOps, Idx>(), ...); > >> + (test_unary_operator<member_ops::UnaryOps, Idx>(), ...); > >> + }; > >> + run(std::make_index_sequence<n_unary_ops>()); > >> +} > >> + > >> +namespace free_ops > >> +{ > >> + template<int OpId> > >> + struct BinaryOps > >> + { > >> + friend constexpr int > >> + operator+(BinaryOps, BinaryOps) noexcept requires (OpId == 0) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator-(BinaryOps, BinaryOps) noexcept requires (OpId == 1) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator*(BinaryOps, BinaryOps) noexcept requires (OpId == 2) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator/(BinaryOps, BinaryOps) noexcept requires (OpId == 3) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator%(BinaryOps, BinaryOps) noexcept requires (OpId == 4) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator<<(BinaryOps, BinaryOps) noexcept requires (OpId == 5) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator>>(BinaryOps, BinaryOps) noexcept requires (OpId == 6) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator&(BinaryOps, BinaryOps) noexcept requires (OpId == 7) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator|(BinaryOps, BinaryOps) noexcept requires (OpId == 8) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator^(BinaryOps, BinaryOps) noexcept requires (OpId == 9) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator&&(BinaryOps, BinaryOps) noexcept requires (OpId == 10) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator||(BinaryOps, BinaryOps) noexcept requires (OpId == 11) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator<=>(BinaryOps, BinaryOps) noexcept requires (OpId == 12) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator<(BinaryOps, BinaryOps) noexcept requires (OpId == 13) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator<=(BinaryOps, BinaryOps) noexcept requires (OpId == 14) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator==(BinaryOps, BinaryOps) noexcept requires (OpId == 15) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator!=(BinaryOps, BinaryOps) noexcept requires (OpId == 16) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator>(BinaryOps, BinaryOps) noexcept requires (OpId == 17) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator>=(BinaryOps, BinaryOps) noexcept requires (OpId == 18) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator+=(BinaryOps, BinaryOps) noexcept requires (OpId == 19) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator-=(BinaryOps, BinaryOps) noexcept requires (OpId == 20) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator*=(BinaryOps, BinaryOps) noexcept requires (OpId == 21) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator/=(BinaryOps, BinaryOps) noexcept requires (OpId == 22) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator%=(BinaryOps, BinaryOps) noexcept requires (OpId == 23) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator&=(BinaryOps, BinaryOps) noexcept requires (OpId == 24) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator|=(BinaryOps, BinaryOps) noexcept requires (OpId == 25) > >> + { return OpId; } > >> + friend constexpr int > >> + > >> + operator^=(BinaryOps, BinaryOps) noexcept requires (OpId == 26) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator<<=(BinaryOps, BinaryOps) noexcept requires (OpId == 27) > >> + { return OpId; } > >> + > >> + friend constexpr int > >> + operator>>=(BinaryOps, BinaryOps) noexcept requires (OpId == 28) > >> + { return OpId; } > >> + }; > >> +} > >> + > >> +namespace member_ops > >> +{ > >> + template<int OpId> > >> + struct BinaryOps > >> + { > >> + constexpr int > >> + operator+(BinaryOps) const noexcept requires (OpId == 0) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator-(BinaryOps) const noexcept requires (OpId == 1) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator*(BinaryOps) const noexcept requires (OpId == 2) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator/(BinaryOps) const noexcept requires (OpId == 3) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator%(BinaryOps) const noexcept requires (OpId == 4) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator<<(BinaryOps) const noexcept requires (OpId == 5) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator>>(BinaryOps) const noexcept requires (OpId == 6) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator&(BinaryOps) const noexcept requires (OpId == 7) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator|(BinaryOps) const noexcept requires (OpId == 8) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator^(BinaryOps) const noexcept requires (OpId == 9) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator&&(BinaryOps) const noexcept requires (OpId == 10) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator||(BinaryOps) const noexcept requires (OpId == 11) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator<=>(BinaryOps) const noexcept requires (OpId == 12) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator<(BinaryOps) const noexcept requires (OpId == 13) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator<=(BinaryOps) const noexcept requires (OpId == 14) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator==(BinaryOps) const noexcept requires (OpId == 15) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator!=(BinaryOps) const noexcept requires (OpId == 16) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator>(BinaryOps) const noexcept requires (OpId == 17) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator>=(BinaryOps) const noexcept requires (OpId == 18) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator+=(BinaryOps) const noexcept requires (OpId == 19) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator-=(BinaryOps) const noexcept requires (OpId == 20) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator*=(BinaryOps) const noexcept requires (OpId == 21) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator/=(BinaryOps) const noexcept requires (OpId == 22) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator%=(BinaryOps) const noexcept requires (OpId == 23) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator&=(BinaryOps) const noexcept requires (OpId == 24) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator|=(BinaryOps) const noexcept requires (OpId == 25) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator^=(BinaryOps) const noexcept requires (OpId == 26) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator<<=(BinaryOps) const noexcept requires (OpId == 27) > >> + { return OpId; } > >> + > >> + constexpr int > >> + operator>>=(BinaryOps) const noexcept requires (OpId == 28) > >> + { return OpId; } > >> + }; > >> +} > >> + > >> +constexpr size_t n_binary_ops = 29; > >> + > >> +template<template<int> typename Ops, int OpId> > >> + constexpr void > >> + test_binary_operator() > >> + { > >> + auto cx = std::cw<Ops<OpId>{}>; > >> + auto cy = std::cw<Ops<OpId>{}>; > >> + > >> + auto check = [](auto c) > >> + { > >> + VERIFY(c == OpId); > >> + static_assert(std::same_as<decltype(c), > >> std::constant_wrapper<OpId>>); > >> + }; > >> + > >> + if constexpr (OpId == 0) > >> + check(cx + cy); > >> + if constexpr (OpId == 1) > >> + check(cx - cy); > >> + if constexpr (OpId == 2) > >> + check(cx * cy); > >> + if constexpr (OpId == 3) > >> + check(cx / cy); > >> + if constexpr (OpId == 4) > >> + check(cx % cy); > >> + if constexpr (OpId == 5) > >> + check(cx << cy); > >> + if constexpr (OpId == 6) > >> + check(cx >> cy); > >> + if constexpr (OpId == 7) > >> + check(cx & cy); > >> + if constexpr (OpId == 8) > >> + check(cx | cy); > >> + if constexpr (OpId == 10) > >> + check(cx && cy); > >> + if constexpr (OpId == 11) > >> + check(cx || cy); > >> + if constexpr (OpId == 12) > >> + check(cx <=> cy); > >> + if constexpr (OpId == 13) > >> + check(cx < cy); > >> + if constexpr (OpId == 14) > >> + check(cx <= cy); > >> + if constexpr (OpId == 15) > >> + check(cx == cy); > >> + if constexpr (OpId == 16) > >> + check(cx != cy); > >> + if constexpr (OpId == 17) > >> + check(cx > cy); > >> + if constexpr (OpId == 18) > >> + check(cx >= cy); > >> + if constexpr (OpId == 19) > >> + check(cx += cy); > >> + if constexpr (OpId == 20) > >> + check(cx -= cy); > >> + if constexpr (OpId == 21) > >> + check(cx *= cy); > >> + if constexpr (OpId == 22) > >> + check(cx /= cy); > >> + if constexpr (OpId == 23) > >> + check(cx %= cy); > >> + if constexpr (OpId == 24) > >> + check(cx &= cy); > >> + if constexpr (OpId == 25) > >> + check(cx |= cy); > >> + if constexpr (OpId == 26) > >> + check(cx ^= cy); > >> + if constexpr (OpId == 27) > >> + check(cx <<= cy); > >> + if constexpr (OpId == 28) > >> + check(cx >>= cy); > >> + static_assert(n_binary_ops == 29); > >> + } > >> + > >> +template<template<int> typename Ops, int OpId> > >> + constexpr void > >> + test_mixed_binary_operators() > >> + { > >> + constexpr auto x = Ops<OpId>{}; > >> + auto cx = std::cw<x>; > >> + constexpr auto y = Ops<OpId>{}; > >> + auto cy = std::cw<y>; > >> + > >> + auto check = [](auto vc, auto cv) > >> + { > >> + auto impl = [](auto c) > >> + { > >> + VERIFY(c == OpId); > >> + static_assert(std::same_as<decltype(c), int>); > >> + }; > >> + > >> + impl(vc); > >> + impl(cv); > >> + }; > >> + > >> + if constexpr (OpId == 0) > >> + check(x + cy, cx + y); > >> + if constexpr (OpId == 1) > >> + check(x - cy, cx - y); > >> + if constexpr (OpId == 2) > >> + check(x * cy, cx * y); > >> + if constexpr (OpId == 3) > >> + check(x / cy, cx / y); > >> + if constexpr (OpId == 4) > >> + check(x % cy, cx % y); > >> + if constexpr (OpId == 5) > >> + check(x << cy, cx << y); > >> + if constexpr (OpId == 6) > >> + check(x >> cy, cx >> y); > >> + if constexpr (OpId == 7) > >> + check(x & cy, cx & y); > >> + if constexpr (OpId == 8) > >> + check(x | cy, cx | y); > >> + if constexpr (OpId == 10) > >> + check(x && cy, cx && y); > >> + if constexpr (OpId == 11) > >> + check(x || cy, cx || y); > >> + if constexpr (OpId == 12) > >> + check(x <=> cy, cx <=> y); > >> + if constexpr (OpId == 13) > >> + check(x < cy, cx < y); > >> + if constexpr (OpId == 14) > >> + check(x <= cy, cx <= y); > >> + if constexpr (OpId == 15) > >> + check(x == cy, cx == y); > >> + if constexpr (OpId == 16) > >> + check(x != cy, cx != y); > >> + if constexpr (OpId == 17) > >> + check(x > cy, cx > y); > >> + if constexpr (OpId == 18) > >> + check(x >= cy, cx >= y); > >> + if constexpr (OpId == 19) > >> + check(x += cy, cx += y); > >> + if constexpr (OpId == 20) > >> + check(x -= cy, cx -= y); > >> + if constexpr (OpId == 21) > >> + check(x *= cy, cx *= y); > >> + if constexpr (OpId == 22) > >> + check(x /= cy, cx /= y); > >> + if constexpr (OpId == 23) > >> + check(x %= cy, cx %= y); > >> + if constexpr (OpId == 24) > >> + check(x &= cy, cx &= y); > >> + if constexpr (OpId == 25) > >> + check(x |= cy, cx |= y); > >> + if constexpr (OpId == 26) > >> + check(x ^= cy, cx ^= y); > >> + if constexpr (OpId == 27) > >> + check(x <<= cy, cx <<= y); > >> + if constexpr (OpId == 28) > >> + check(x >>= cy, cx >>= y); > >> + static_assert(n_binary_ops == 29); > >> + } > >> + > >> +constexpr void > >> +test_binary_operators_all() > >> +{ > >> + auto run = []<size_t... Idx>(std::integer_sequence<size_t, Idx...>) > >> + { > >> + (test_binary_operator<free_ops::BinaryOps, Idx>(), ...); > >> + (test_mixed_binary_operators<free_ops::BinaryOps, Idx>(), ...); > >> + (test_binary_operator<member_ops::BinaryOps, Idx>(), ...); > >> + }; > >> + run(std::make_index_sequence<n_binary_ops>()); > >> +} > >> + > >> +constexpr bool > >> +test_all() > >> +{ > >> + test_unary_operators_all(); > >> + test_binary_operators_all(); > >> + return true; > >> +} > >> + > >> +int > >> +main() > >> +{ > >> + test_all(); > >> + return 0; > >> +} > >> diff --git > >> a/libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc > >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc > >> new file mode 100644 > >> index 00000000000..4384e920ea5 > >> --- /dev/null > >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc > >> @@ -0,0 +1,14 @@ > >> +// { dg-do compile { target c++26 } } > >> +#include <type_traits> > >> + > >> +constexpr void > >> +test_comma_same_types() > >> +{ > >> + (std::cw<1>, std::cw<2>); // { dg-error "use of deleted function" } > >> +} > >> + > >> +constexpr void > >> +test_comma_different_types() > >> +{ > >> + (std::cw<1>, std::cw<2.0>); // { dg-error "use of deleted function" } > >> +} > >> diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc > >> b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc > >> new file mode 100644 > >> index 00000000000..4fee6159141 > >> --- /dev/null > >> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc > >> @@ -0,0 +1,11 @@ > >> +// { dg-do preprocess { target c++26 } } > >> +// { dg-add-options no_pch } > >> + > >> +#include <type_traits> > >> + > >> +#ifndef __cpp_lib_constant_wrapper > >> +#error "Feature test macro __cpp_lib_constant_wrapper is missing for > >> <type_traits>" > >> +#if __cpp_lib_constant_wrapper < 202506L > >> +#error "Feature test macro __cpp_lib_constant_wrapper has the wrong > value" > >> +#endif > >> +#endif > >> -- > >> 2.51.0 > >> > >> > > > >
