On Tue, Sep 2, 2025 at 10:07 AM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:
This is a partial implementation of P2781R9. It adds std::cw and
std::constant_wrapper, but doesn't modify __integral_constant_like for
span/mdspan.
libstdc++-v3/ChangeLog:
* include/bits/version.def (constant_wrapper): Add.
* include/bits/version.h: Regenerate.
* include/std/type_traits (_CwFixedValue): New class.
(_IndexSequence): New struct.
(_BuildIndexSequence): New struct.
(_ConstExprParam): New concept.
(_CwOperators): New struct.
(constant_wrapper): New struct.
(cw): New global constant.
* src/c++23/std.cc.in (constant_wrapper): Add.
(cw): Add.
* testsuite/20_util/constant_wrapper/adl.cc: New test.
* testsuite/20_util/constant_wrapper/ex.cc: New test.
* testsuite/20_util/constant_wrapper/generic.cc: New test.
* testsuite/20_util/constant_wrapper/instantiate.cc: New test.
* testsuite/20_util/constant_wrapper/op_comma_neg.cc: New test.
* testsuite/20_util/constant_wrapper/version.cc: New test.
Signed-off-by: Luc Grosheintz <luc.groshei...@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.
---
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.
+ 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
+ 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