[PATCH v2 2/3] libstdc++: Implement constant_wrapper, cw from P2781R9.
Luc Grosheintz
luc.grosheintz@gmail.com
Tue Sep 2 16:22:09 GMT 2025
On 9/2/25 13:45, Tomasz Kaminski wrote:
> On Tue, Sep 2, 2025 at 10:07 AM Luc Grosheintz <luc.grosheintz@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.
>>
>> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
>>
> 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>?
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.
>
>
> + 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
>>
>>
>
More information about the Libstdc++
mailing list