On Sat, Jun 13, 2026 at 8:13 PM Jonathan Wakely <[email protected]> wrote:
> On Mon, 8 Jun 2026 at 16:27, Tomasz Kamiński <[email protected]> wrote: > > > > We need to apply remove_cvref_t on decltype(_Xv) for default template > argument > > due PR115314. The constant_wrapper::value is declared as > decltype((__Xv)) due > > PR125188. > > > > libstdc++-v3/ChangeLog: > > > > * include/bits/version.def (constant_wrapper): Bump to 202606L. > > * include/bits/version.h: Regenerate. > > * include/bits/funcref_impl.h (function_ref::function_ref): > Rename > > template parameter from __cwfn to __fn and use it direclty. > > * include/bits/funcwrap.h (function_ref): Rename template > parameter > > to __fn. > > (std::constant_wrapper): Use auto as non-type template parameter, > > and refeference it as value. > > * include/bits/utility.h (__CwFixedValue): Remove. > > * testsuite/20_util/constant_wrapper/generic.cc: Remove arrays > > and string literal tests. Add test for address of value. > > * testsuite/20_util/constant_wrapper/other_wrappers.cc: > > Remove test_array. > > > > Reviewed-by: Patrick Palka <[email protected]> > > --- > > This was just apporved by LWG in Brno. > > Since RFC added FTM with the value corresponding to Brno. > > > > Tested on x86_64-linux. OK for trunk and 16 when approved > > in plenary. > > OK for trunk and gcc-16, thanks. > This was merged for GCC-16 after Patrick's backport of r17-117-gc6 > > > > > libstdc++-v3/include/bits/funcref_impl.h | 15 ++-- > > libstdc++-v3/include/bits/funcwrap.h | 8 +-- > > libstdc++-v3/include/bits/utility.h | 50 +++---------- > > libstdc++-v3/include/bits/version.def | 5 +- > > libstdc++-v3/include/bits/version.h | 4 +- > > .../20_util/constant_wrapper/generic.cc | 72 ++++++------------- > > .../constant_wrapper/other_wrappers.cc | 14 ---- > > .../constant_wrapper/type_param_neg.cc | 9 +++ > > 8 files changed, 55 insertions(+), 122 deletions(-) > > create mode 100644 > libstdc++-v3/testsuite/20_util/constant_wrapper/type_param_neg.cc > > > > diff --git a/libstdc++-v3/include/bits/funcref_impl.h > b/libstdc++-v3/include/bits/funcref_impl.h > > index db85f173aab..8a00fde1b8e 100644 > > --- a/libstdc++-v3/include/bits/funcref_impl.h > > +++ b/libstdc++-v3/include/bits/funcref_impl.h > > @@ -132,12 +132,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > // _GLIBCXX_RESOLVE_LIB_DEFECTS > > // 4256. Incorrect constrains for function_ref constructors from > nontype > > /// Target object is __fn. There is no bound object. > > - template<auto __cwfn, typename _Fn> > > + template<auto __fn, typename _Fn> > > requires __is_invocable_using<const _Fn&> > > constexpr > > - function_ref(constant_wrapper<__cwfn, _Fn>) noexcept > > + function_ref(constant_wrapper<__fn, _Fn>) noexcept > > { > > - constexpr const _Fn& __fn = constant_wrapper<__cwfn, > _Fn>::value; > > if constexpr (sizeof...(_ArgTypes) > 0) > > if constexpr ((... && > _ConstExprParam<remove_cvref_t<_ArgTypes>>)) > > static_assert(!requires { > > @@ -153,14 +152,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > /// Target object is equivalent to > std::bind_front<_fn>(std::ref(__ref)). > > /// Bound object is object referenced by second parameter. > > - template<auto __cwfn, typename _Fn, typename _Up, > > + template<auto __fn, typename _Fn, typename _Up, > > typename _Td = remove_reference_t<_Up>> > > requires (!is_rvalue_reference_v<_Up&&>) > > && __is_invocable_using<const _Fn&, _Td > _GLIBCXX_MOF_CV&> > > constexpr > > - function_ref(constant_wrapper<__cwfn, _Fn>, _Up&& __ref) noexcept > > + function_ref(constant_wrapper<__fn, _Fn>, _Up&& __ref) noexcept > > { > > - constexpr const _Fn& __fn = constant_wrapper<__cwfn, > _Fn>::value; > > if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) > > static_assert(__fn != nullptr); > > > > @@ -176,12 +174,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > /// Target object is equivalent to std::bind_front<_fn>(__ptr). > > /// Bound object is object pointed by second parameter (if any). > > - template< auto __cwfn, typename _Fn, typename _Td> > > + template<auto __fn, typename _Fn, typename _Td> > > requires __is_invocable_using<const _Fn&, _Td _GLIBCXX_MOF_CV*> > > constexpr > > - function_ref(constant_wrapper<__cwfn, _Fn>, _Td _GLIBCXX_MOF_CV* > __ptr) noexcept > > + function_ref(constant_wrapper<__fn, _Fn>, _Td _GLIBCXX_MOF_CV* > __ptr) noexcept > > { > > - constexpr const _Fn& __fn = constant_wrapper<__cwfn, > _Fn>::value; > > if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) > > static_assert(__fn != nullptr); > > if constexpr (is_member_pointer_v<_Fn>) > > diff --git a/libstdc++-v3/include/bits/funcwrap.h > b/libstdc++-v3/include/bits/funcwrap.h > > index 07163f3a5a3..670efa63b4a 100644 > > --- a/libstdc++-v3/include/bits/funcwrap.h > > +++ b/libstdc++-v3/include/bits/funcwrap.h > > @@ -592,16 +592,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > requires is_function_v<_Fn> > > function_ref(_Fn*) -> function_ref<_Fn>; > > > > - template<auto __cwfn, typename _Fn> > > + template<auto __fn, typename _Fn> > > requires is_function_v<remove_pointer_t<_Fn>> > > - function_ref(constant_wrapper<__cwfn, _Fn>) > > + function_ref(constant_wrapper<__fn, _Fn>) > > -> function_ref<remove_pointer_t<_Fn>>; > > > > - template<auto __cwfn, typename _Fn, typename _Tp, > > + template<auto __fn, typename _Fn, typename _Tp, > > typename _SignaturePtr = > > decltype(__polyfunc::__deduce_funcref<_Fn, _Tp&>())> > > requires (!is_void_v<_SignaturePtr>) > > - function_ref(constant_wrapper<__cwfn, _Fn>, _Tp&&) > > + function_ref(constant_wrapper<__fn, _Fn>, _Tp&&) > > -> function_ref<remove_pointer_t<_SignaturePtr>>; > > > > /// @cond undocumented > > diff --git a/libstdc++-v3/include/bits/utility.h > b/libstdc++-v3/include/bits/utility.h > > index dac02e4a479..320dd507b43 100644 > > --- a/libstdc++-v3/include/bits/utility.h > > +++ b/libstdc++-v3/include/bits/utility.h > > @@ -141,42 +141,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > #endif > > > > #ifdef __glibcxx_constant_wrapper // C++ >= 26 > > - template<typename _Tp> > > - struct _CwFixedValue > > - { > > - using __type = _Tp; > > - > > - constexpr > > - _CwFixedValue(__type __v) noexcept > > - : _M_data(__v) { } > > - > > - __type _M_data; > > - }; > > - > > - template<typename _Tp, size_t _Extent> > > - struct _CwFixedValue<_Tp[_Extent]> > > - { > > - using __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 _Xv, > > - typename = typename decltype(_CwFixedValue(_Xv))::__type> > > + // remove_cvref_t needed due PR115314 > > + template<auto _Xv, typename _Vt = remove_cvref_t<decltype(_Xv)>> > > struct constant_wrapper; > > > > template<typename _Tp> > > @@ -430,12 +396,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > { return {}; } > > }; > > > > - template<_CwFixedValue _Xv, typename> > > + template<auto _Xv, typename _Vt> > > struct constant_wrapper : _CwOperators > > { > > - static constexpr const auto& value = _Xv._M_data; > > + // Use decltype((_Xv)) instead of decltype(auto) due PR125188 > > + static constexpr decltype((_Xv)) value = (_Xv); > > using type = constant_wrapper; > > - using value_type = typename decltype(_Xv)::__type; > > + using value_type = decltype(_Xv); > > + static_assert(is_same_v<value_type, _Vt>); > > > > template<_ConstExprParam _Right> > > constexpr auto > > @@ -490,8 +458,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > template<auto __cw, typename _Fn> > > constexpr bool __is_constant_wrapper_v<constant_wrapper<__cw, _Fn>> > = true; > > > > - template<_CwFixedValue _Tp> > > - constexpr auto cw = constant_wrapper<_Tp>{}; > > + template<auto _Xv> > > + constexpr auto cw = constant_wrapper<_Xv>{}; > > #endif > > > > #ifdef __glibcxx_integer_sequence // C++ >= 14 > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > > index 5dddf4cf345..40baefab4e4 100644 > > --- a/libstdc++-v3/include/bits/version.def > > +++ b/libstdc++-v3/include/bits/version.def > > @@ -410,9 +410,10 @@ ftms = { > > name = constant_wrapper; > > // 202506 P2781R9 std::constexpr_wrapper > > // 202603 P3978R3 constant_wrapper should unwrap on call and subscript > > + // 202606 P4206R0 Revert string support in std::constant_wrapper > > values = { > > - v = 202603; > > - cxxmin = 26; > > + v = 202606; > > + cxxmin = 26; // P4206R0 accepted as DR for C++26 > > }; > > }; > > > > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > > index 787315033bb..de1d71fa935 100644 > > --- a/libstdc++-v3/include/bits/version.h > > +++ b/libstdc++-v3/include/bits/version.h > > @@ -447,9 +447,9 @@ > > > > #if !defined(__cpp_lib_constant_wrapper) > > # if (__cplusplus > 202302L) > > -# define __glibcxx_constant_wrapper 202603L > > +# define __glibcxx_constant_wrapper 202606L > > # if defined(__glibcxx_want_all) || > defined(__glibcxx_want_constant_wrapper) > > -# define __cpp_lib_constant_wrapper 202603L > > +# define __cpp_lib_constant_wrapper 202606L > > # endif > > # endif > > #endif /* !defined(__cpp_lib_constant_wrapper) */ > > diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > > index bd41fc4ca3a..eb0a86d9095 100644 > > --- a/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > > @@ -12,54 +12,6 @@ check_same(auto actual, auto expected) > > static_assert(std::same_as<decltype(actual), decltype(expected)>); > > }; > > > > - > > -constexpr void > > -test_c_arrays() > > -{ > > - constexpr double x[] = {1.1, 2.2, 3.3}; > > - auto cx = std::cw<x>; > > - auto access = [](auto x, size_t i) > > - { return x[i]; }; > > - > > - check_same(std::cw<x>[0], x[0]); > > - check_same(std::cw<x>[1], x[1]); > > - check_same(std::cw<x>[2], x[2]); > > - > > - check_same(cx[std::cw<0>], std::cw<x[0]>); > > - check_same(cx[std::cw<1>], std::cw<x[1]>); > > - check_same(cx[std::cw<2>], std::cw<x[2]>); > > -} > > - > > -constexpr size_t > > -deduce_cstr_size(auto str) > > -{ > > - size_t sz = 0; > > - while(str[sz++] != '\0') { } > > - return sz; > > -} > > - > > -constexpr void > > -test_string_literals() > > -{ > > - auto foo = std::cw<"foo">; > > - constexpr const typename decltype(foo)::value_type & cstr = foo; > > - constexpr size_t N = std::size(cstr); > > - constexpr auto foo_view = std::string_view(cstr, N-1); > > - > > - constexpr const char (&cstr1)[deduce_cstr_size(foo)] = foo; > > - constexpr size_t N1 = std::size(cstr); > > - > > - static_assert(static_cast<char const*>(cstr) == > > - static_cast<char const*>(cstr1)); > > - static_assert(N1 == N); > > - > > - static_assert(foo[0] == 'f'); > > - static_assert(foo[1] == 'o'); > > - static_assert(foo[2] == 'o'); > > - static_assert(foo[3] == '\0'); > > - static_assert(static_cast<char const *>(foo) == foo_view); > > -} > > - > > constexpr bool > > convert_constexpr(auto c) > > { > > @@ -93,6 +45,27 @@ test_ints() > > VERIFY(two + 3 == std::cw<5>); > > } > > > > +constexpr void > > +test_objects() > > +{ > > + if consteval { > > + return; > > + } > > + > > + auto check = []<auto V> > > + { > > + VERIFY(&V == &std::constant_wrapper<V>::value); > > + std::constant_wrapper<V> cw; > > + VERIFY(&V == &cw.value); > > + }; > > + > > + struct Obj > > + { int x; }; > > + > > + check.operator()<Obj{10}>(); > > + check.operator()<Obj{20}>(); > > +} > > + > > constexpr int > > add(int i, int j) > > { return i + j; } > > @@ -586,12 +559,11 @@ test_assignment() > > std::cw<ConstAssignable{2}>); > > } > > > > - > > constexpr bool > > test_all() > > { > > - test_c_arrays(); > > test_ints(); > > + test_objects(); > > test_function_object(); > > test_function_pointer(); > > test_indexable1(); > > diff --git > a/libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc > b/libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc > > index fe45c5a8516..a5e2189ef80 100644 > > --- a/libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc > > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc > > @@ -30,19 +30,6 @@ test_mix_integer_constant() > > check_same(w2 + c3, std::cw<5>); > > } > > > > -constexpr void > > -test_array() > > -{ > > - constexpr double x[] = {1.1, 2.2, 3.3}; > > - auto cx = std::cw<x>; > > - auto i2 = std::integral_constant<int, 2>{}; > > - auto w2 = ConstWrapper<int, 2>{}; > > - > > - check_same(x[i2], x[2]); > > - check_same(cx[i2], std::cw<x[2]>); > > - check_same(cx[w2], std::cw<x[2]>); > > -} > > - > > constexpr void > > test_function_object() > > { > > @@ -61,7 +48,6 @@ constexpr bool > > test_all() > > { > > test_mix_integer_constant(); > > - test_array(); > > test_function_object(); > > return true; > > } > > diff --git > a/libstdc++-v3/testsuite/20_util/constant_wrapper/type_param_neg.cc > b/libstdc++-v3/testsuite/20_util/constant_wrapper/type_param_neg.cc > > new file mode 100644 > > index 00000000000..1548e0789e1 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/type_param_neg.cc > > @@ -0,0 +1,9 @@ > > +// { dg-do compile { target c++26 } } > > +#include <utility> > > + > > +std::constant_wrapper<1, float> c1; // { dg-error "from here" } > > +std::constant_wrapper<1.0, int> c2; // { dg-error "from here" } > > +std::constant_wrapper<1, int const> c3; // { dg-error "from here" } > > +std::constant_wrapper<1, int const&> c4; // { dg-error "from here" } > > + > > +// { dg-prune-output "static assertion failed" } > > -- > > 2.54.0 > > > >
