Two more comments (see below).

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.

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

I think you've uncovered something. None of these produce a
constant_wrapper. They all result in an int.

Let's define the following:

  constexpr int nom = 42;
  constexpr int denom = 3;

  auto cdiv = std::cw<&Divide::divide>;
  auto co = std::cw<Divide{nom}>;

  [[maybe_unused]] auto check = [](auto actual, auto expected)
  {
    VERIFY(actual == expected);
    static_assert(std::same_as<decltype(actual), decltype(expected)>);
  };

  [[maybe_unused]] auto expect_wrapped = std::cw<nom / denom>;
  [[maybe_unused]] auto expect_unwrapped = nom / denom;

then

  auto x = (&co)->* cdiv;

results in the following compiler error GCC:

/home/lucg/git/gcc/src-master/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc:216: error: unable to deduce 'auto' from '((const 
Divide*)std::operator&<constant_wrapper<_CwFixedValue<Divide>{_CwFixedValue<Divide>::_S_type{42}}, Divide> >((co, 
std::constant_wrapper<std::_CwFixedValue<Divide>{std::_CwFixedValue<Divide>::_S_type{42}}, Divide>())).std::constant_wrapper<std::_CwFixedValue<const 
Divide*>{(& std::_CwFixedValue<Divide>{std::_CwFixedValue<Divide>::_S_type{42}}.std::_CwFixedValue<Divide>::_M_data)}, const Divide*>::operator const Divide* 
const&())->*cdiv.std::constant_wrapper<std::_CwFixedValue<int (Divide::*)(int) const>{&Divide::divide}, int (Divide::*)(int) const>::operator int (Divide::* 
const&)(int) const()'
/home/lucg/git/gcc/src-master/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc:216:
 note:   member function type 'int (Divide::)(int) const' is not a valid 
template argument

and clang:

<source>:414:18: error: right hand operand to ->* has non-pointer-to-member type 
'constant_wrapper<_CwFixedValue<int (Divide::*)(int) const>{&Divide::divide}>'
  414 |   auto x = ((&co)->* cdiv);
      |                  ^   ~~~~
<source>:227:7: note: candidate template ignored: substitution failure [with _Left = 
constant_wrapper<(&std::constant_wrapper<_CwFixedValue<Divide>{{42}}, Divide>::value)>, _Right = 
constant_wrapper<_CwFixedValue<int (Divide::*)(int) const>{&Divide::divide}>]: no viable constructor or 
deduction guide for deduction of template arguments of '_CwFixedValue'
  227 |       operator->*(_Left, _Right) noexcept
      |       ^
  228 |         -> constant_wrapper<_Left::value->*(_Right::value)>
      |                             ~~~~~
1 error generated.

see: https://godbolt.org/z/bzaMrr8Ps

The slightly modified version:

  auto x = ((&co)->* cdiv)(denom);
  static_assert(std::same_as<decltype(x), int>);

compiles fine in GCC, but not in Clang, as do all of the following (not
checked with clang):

  check(((&co)->* cdiv)(std::cw<denom>), expect_unwrapped);
  check(((&co)->* cdiv)(denom), expect_unwrapped);
  check(((&co)->*(&Divide::divide))(denom), expect_unwrapped);
  check((&(co.value)->*cdiv)(denom), expect_unwrapped);
  check(((&decltype(co)::value)->*cdiv)(denom), expect_unwrapped);


+}
+
+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.

None of these are, because:

    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 {}; }

and bool is constructible from `_Left::value` (aka bool). Instead, I
can add a check that at least `ctrue && ctrue` is a core constant, i.e.

  static_assert(ctrue || cfalse);

works even if ctrue and cfalse are function arguments.


+  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




Reply via email to