On 9/4/25 11:40, Tomasz Kaminski wrote:
On Wed, Sep 3, 2025 at 4:54 PM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:



On 9/2/25 13:45, Tomasz Kaminski wrote:
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.

(I'm double checking the message, "just before" submitting v3, and keep
finding more. Sorry, about many separate messages.)

If I understand correctly, this depends. If the object defines it's
operators are free friend functions, then ADL kicks in and mixing
wrapped and unwrapped types works. OTOH, if the operators are member
functions (methods), then ADL doesn't kick in and it's not possible
to mix (this is mentioned in the paper [1]). This is already tested in
`instantiate.cc`. (I'll mark it below with `Here: diowe`).

Hmm, this goes sidewise to what I would expect. The example on paper,
relies on fact that for class template, a hidden-friend in not templated
function
(so conversions are possible), while outside of class, is a templated one
and
we cannot deduce the template parameter. For member functions conversion
to object parameters (this) are not possible, however they always work with
second argument.

This is test cases I have tested, to confirm that my head compiler is right
on
this:
// { dg-do run { target c++26 } }
#include <concepts>
#include <type_traits>

#include <testsuite_hooks.h>

namespace adl {

struct Friend
{};

constexpr
int operator+(Friend, int x)
{ return x; };

template<typename T>
struct TemplFriend
{};

template<typename T>
constexpr
// templated, we cannot deduce T from cw<Friend<int>>
int operator+(TemplFriend<T>, int x)
{ return x; };


struct HiddenFriend
{
   constexpr friend
   int operator+(HiddenFriend, int x)
   { return x; }
};

template<typename T>
struct TemplHiddenFriend
{
   constexpr friend
   // note that this not not template itself
   int operator+(TemplHiddenFriend, int x)
   { return x; }
};

}

template<typename T>
concept supportMixedObj = requires
{
   { std::cw<T{}> + 1 } -> std::same_as<int>;
};

template<typename T>
concept supportMixedInt = requires(T t)
{
   { t + std::cw<1> } -> std::same_as<int>;
};


static_assert(supportMixedObj<adl::Friend>);
static_assert(supportMixedInt<adl::Friend>);
static_assert(!supportMixedObj<adl::TemplFriend<int>>);
static_assert(supportMixedInt<adl::TemplFriend<int>>);

static_assert(supportMixedObj<adl::HiddenFriend>);
static_assert(supportMixedInt<adl::HiddenFriend>);
static_assert(supportMixedObj<adl::TemplHiddenFriend<int>>);
static_assert(supportMixedInt<adl::TemplHiddenFriend<int>>);

struct Member
{
   constexpr
   // conversion for the first argument is not allowed
   int operator+(int x) const
   { return x; }
};

static_assert(!supportMixedObj<Member>);
static_assert(supportMixedInt<Member>);

struct ExplicitThisMember
{
   constexpr
   // conversion for the first argument is not allowed
   int operator+(this ExplicitThisMember, int x)
   { return x; }
};

static_assert(!supportMixedObj<ExplicitThisMember>);
static_assert(supportMixedInt<ExplicitThisMember>);



Thank you, indeed I was inaccurate/wrong, due to having exactly the
case of a template class (and failing deduction). What you write is
correct.

The currently prepped v3 doesn't have an adl.cc anymore, since it
didn't do more than instantiate.cc. Do you want to add your tests
separately on top of v3 (I don't want to steal them, they're quite
elaborate)?




[1]:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2781r9.html#the-difference-in-template-parameters-to-stdconstant_wrapper-and-stdcw


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

+}
+
+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);
+  }
+

Here: diowe The following test checks that we can
mix wrapped and unwrapped types (if using friend
functions to overload the operators).
+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







Reply via email to