On Tue, 5 May 2026, Tomasz Kamiński wrote:

> We need to apply remove_cvref_t on decltype(_Xv) for default template
> argument due PR117004. The constant_wrapper::value is declared as
> decltype((__Xv)) due PR125188.
> 
> libstdc++-v3/ChangeLog:
> 
>       * 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.
> ---
> Implemented it to double check decltype(auto) behavior in wording, 
> seem correct, just affected by GCC bug.
> 
> Tested on x86_64-linux locally.
> 
>  libstdc++-v3/include/bits/funcref_impl.h      | 15 ++--
>  libstdc++-v3/include/bits/funcwrap.h          |  8 +-
>  libstdc++-v3/include/bits/utility.h           | 50 +++----------
>  .../20_util/constant_wrapper/generic.cc       | 74 ++++++-------------
>  .../constant_wrapper/other_wrappers.cc        | 14 ----
>  .../constant_wrapper/type_param_neg.cc        |  9 +++
>  6 files changed, 52 insertions(+), 118 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 9fcab570803..af4313b0719 100644
> --- a/libstdc++-v3/include/bits/funcref_impl.h
> +++ b/libstdc++-v3/include/bits/funcref_impl.h
> @@ -129,12 +129,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 {
> @@ -151,14 +150,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);
>  
> @@ -174,12 +172,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 b835e075295..a7f274f98bd 100644
> --- a/libstdc++-v3/include/bits/funcwrap.h
> +++ b/libstdc++-v3/include/bits/funcwrap.h
> @@ -573,16 +573,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>>;
>  
>  #endif // __glibcxx_function_ref
> diff --git a/libstdc++-v3/include/bits/utility.h 
> b/libstdc++-v3/include/bits/utility.h
> index dac02e4a479..9bfc0c3d17f 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 PR117004
> +  template<auto _Xv, typename = remove_cvref_t<decltype(_Xv)>>

We should refer to PR115314 instead of the closed dup PR117004.
I would expect remove_const_t is sufficient here, but it seems
PR115314 can also manifest as unexpected reference qualifiers
too: https://gcc.gnu.org/PR115314#c6

While we're modifying this forward declaration, we should give the
second template parameter a name so that pretty printing the template
gives:

  std::constant_wrapper<_Xv, _Vt>

instead of

  std::constant_wrapper<_Xv, <template-parameter-1-2> >

For a class template GCC pretty prints the template parameters of
the first (possibly forward) declaration rather than those of the
definition.

>      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/testsuite/20_util/constant_wrapper/generic.cc 
> b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc
> index bd41fc4ca3a..f256e08fe0b 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,29 @@ test_ints()
>    VERIFY(two + 3 == std::cw<5>);
>  }
>  
> +constexpr void
> +test_objects()
> +{
> +  if consteval {
> +    return;
> +  }
> +
> +  auto check = []<auto V>
> +  {
> +    auto* ptr1 = &V, *ptr2= &std::constant_wrapper<V>::value;
> +    VERIFY(ptr1 == ptr2); 
> +    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 +561,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..48895091611
> --- /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
> 
> 

Reply via email to