[PATCH v1][RFC] libstdc++: Implement constant_wrapper, cw from P2781R8.
Tomasz Kaminski
tkaminsk@redhat.com
Thu Aug 14 11:47:42 GMT 2025
On Wed, Aug 13, 2025 at 12:24 PM Luc Grosheintz <luc.grosheintz@gmail.com>
wrote:
> This is a partial implementation of P2781R8. 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.
> * 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/version.cc: New test.
>
> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
> ---
>
> This is a preview to discuss certain points:
>
> 1. Is there a better way of initializing one C array with
> another?
>
I was thinking about it, but haven't come up with anything better.
>
> 2. If no, should we refactor <utility>? If we include <utility> from
> <type_traits> we're creating a circular dependency (<utility> includes
> <type_traits>). One solution is to lift the required code into
> a <bits/integer_sequence.h> and then include that file from both
> <utility> and <type_traits>.
>
This seems like a good direction for me.
>
> 3. Is the type of test that will "check" each operator meaningful?
>
For me it would be important to test member and non-member, there was a lot
of issues in the original wording, where the non-member version was assumed.
For test, I would test, if:
operator on cw<> produce cw<>
operator on mix of cw<> and not-wrapped produce integers
The mix of cw and non-wrapped should work for non-assignment operators,
but I do not think compound assignment, or subscript are supported.
There is interesting case for call operator, where non-cw calls are
supported
for functions pointers, but not for functors:
cw<function-pointer>(1, 2) -> calls function-pointer(1, 2)
>
> libstdc++-v3/include/bits/version.def | 8 +
> libstdc++-v3/include/bits/version.h | 10 +
> libstdc++-v3/include/std/type_traits | 354 ++++++++++++++++++
> .../testsuite/20_util/constant_wrapper/adl.cc | 42 +++
> .../testsuite/20_util/constant_wrapper/ex.cc | 45 +++
> .../20_util/constant_wrapper/generic.cc | 78 ++++
> .../20_util/constant_wrapper/version.cc | 11 +
> 7 files changed, 548 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/version.cc
>
> diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> index dbe2cb8f175..c8253e52168 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 7bb6016df68..c0512fe6579 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 ff23544fbf0..0c87b5bf444 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
> @@ -4280,6 +4281,359 @@ template<typename _Ret, typename _Fn, typename...
> _Args>
>
> #endif // C++2a
>
> +#ifdef __cpp_lib_constant_wrapper // C++ >= 26
> + template<typename _Tp>
> + struct _CwFixedValue
> + {
> + using _S_type = _Tp;
> +
> + constexpr
> + _CwFixedValue(_S_type __v) noexcept
> + : _M_data(__v) { }
> +
> + _S_type _M_data;
> + };
> +
> + template<size_t... _Indices>
> + struct _IndexSequence
> + { };
> +
> + template<size_t _Nm, size_t... _Indices>
> + struct _BuildIndexSequence
> + {
> + using _S_type = _BuildIndexSequence<_Nm, _Indices...,
> sizeof...(_Indices)>::_S_type;
> + };
> +
> + template<size_t _Nm, size_t... _Indices>
> + requires (sizeof...(_Indices) == _Nm)
> + struct _BuildIndexSequence<_Nm, _Indices...>
> + {
> + using _S_type = _IndexSequence<_Indices...>;
> + };
> +
> + template<typename _Tp, size_t _Extent>
> + struct _CwFixedValue<_Tp[_Extent]> {
> + using _S_type = _Tp[_Extent];
> +
> + constexpr
> + _CwFixedValue(_Tp (&__arr)[_Extent]) noexcept
> + : _CwFixedValue(__arr, typename
> _BuildIndexSequence<_Extent>::_S_type())
> + { }
> +
> + template<size_t... _Indices>
> + constexpr
> + _CwFixedValue(_Tp (&__arr)[_Extent], _IndexSequence<_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 {}; }
> +
> + // pseudo-mutators
> + 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/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 } }
> +#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()
> +// {
> +// 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..4381ac43639
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc
> @@ -0,0 +1,78 @@
> +// { dg-do run { target c++26 } }
> +#include <type_traits>
> +#include <utility>
> +
> +#include <testsuite_hooks.h>
> +
> +constexpr void
> +test_c_arrays()
> +{
> + 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;
> + VERIFY(two + 3 == 5);
> +}
> +
> +template<int OpId>
> + struct Int
> + {
> + friend constexpr int
> + operator+(Int) noexcept requires(OpId == 0)
> + { return OpId; }
> +
> + friend constexpr int
> + operator-(Int) noexcept requires(OpId == 1)
> + { return OpId; }
> +
> + int value;
> + };
> +
> +template<int OpId>
> + constexpr void
> + test_unary_operator()
> + {
> + auto x = std::cw<Int<OpId>{-1}>;
> + if constexpr (OpId == 0)
> + VERIFY((+x).value == OpId);
> + if constexpr (OpId == 1)
> + VERIFY((-x).value == OpId);
> + }
> +
> +constexpr void
> +test_unary_operator_all()
> +{
> + auto run_all = []<size_t... Idx>(std::integer_sequence<size_t, Idx...>)
> + {
> + (test_unary_operator<Idx>(), ...);
> + };
> + run_all(std::make_index_sequence<2>());
> +}
> +
> +constexpr bool
> +test_all()
> +{
> + test_c_arrays();
> + test_ints();
> + test_unary_operator_all();
> + return true;
> +}
> +
> +int
> +main()
> +{
> + test_all();
> + static_assert(test_all());
> + return 0;
> +}
> diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc
> b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc
> new file mode 100644
> index 00000000000..4fee6159141
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc
> @@ -0,0 +1,11 @@
> +// { dg-do preprocess { target c++26 } }
> +// { dg-add-options no_pch }
> +
> +#include <type_traits>
> +
> +#ifndef __cpp_lib_constant_wrapper
> +#error "Feature test macro __cpp_lib_constant_wrapper is missing for
> <type_traits>"
> +#if __cpp_lib_constant_wrapper < 202506L
> +#error "Feature test macro __cpp_lib_constant_wrapper has the wrong value"
> +#endif
> +#endif
> --
> 2.50.0
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250814/903c23c5/attachment-0001.htm>
More information about the Libstdc++
mailing list