https://gcc.gnu.org/g:7bafcc36172b572296731df951df4b0caa019b95
commit r16-7495-g7bafcc36172b572296731df951df4b0caa019b95 Author: Jonathan Wakely <[email protected]> Date: Wed Feb 11 22:39:52 2026 +0000 libstdc++: Make CTAD ignore tuple(const Types&...) constructor [PR121771] This is similar to the r16-3536-g0bb0d1d2880d56 change for std::pair, so that CTAD ignores the tuple(const Types&...) constructor and only uses the tuple(Types...) -> tuple<Types...> deduction guide. This ensures that the deduced type comes from the decayed argument types. libstdc++-v3/ChangeLog: PR libstdc++/121771 * include/std/tuple (tuple::tuple(const Elements&...)): Use type_identity_t to prevent constructor being used for CTAD. (tuple::tuple(allocator_arg_t, const A&, const Elements&...)): Likewise. * testsuite/20_util/tuple/cons/121771.cc: New test. Reviewed-by: Ville Voutilainen <[email protected]> Diff: --- libstdc++-v3/include/std/tuple | 12 +++--- .../testsuite/20_util/tuple/cons/121771.cc | 45 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 7f8cb7cbb1f9..f7caa79cda04 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -969,7 +969,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Defined as a template to work around PR libstdc++/116440. template<typename = void> constexpr explicit(!__convertible<const _Elements&...>()) - tuple(const _Elements&... __elements) + tuple(const type_identity_t<_Elements>&... __elements) noexcept(__nothrow_constructible<const _Elements&...>()) requires (__constructible<const _Elements&...>()) : _Inherited(__elements...) @@ -1161,7 +1161,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Alloc> constexpr explicit(!__convertible<const _Elements&...>()) tuple(allocator_arg_t __tag, const _Alloc& __a, - const _Elements&... __elements) + const type_identity_t<_Elements>&... __elements) requires (__constructible<const _Elements&...>()) : _Inherited(__tag, __a, __elements...) { } @@ -1470,14 +1470,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<bool _NotEmpty = (sizeof...(_Elements) >= 1), _ImplicitCtor<_NotEmpty, const _Elements&...> = true> constexpr - tuple(const _Elements&... __elements) + tuple(const __type_identity_t<_Elements>&... __elements) noexcept(__nothrow_constructible<const _Elements&...>()) : _Inherited(__elements...) { } template<bool _NotEmpty = (sizeof...(_Elements) >= 1), _ExplicitCtor<_NotEmpty, const _Elements&...> = false> explicit constexpr - tuple(const _Elements&... __elements) + tuple(const __type_identity_t<_Elements>&... __elements) noexcept(__nothrow_constructible<const _Elements&...>()) : _Inherited(__elements...) { } @@ -1562,7 +1562,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _ImplicitCtor<_NotEmpty, const _Elements&...> = true> _GLIBCXX20_CONSTEXPR tuple(allocator_arg_t __tag, const _Alloc& __a, - const _Elements&... __elements) + const __type_identity_t<_Elements>&... __elements) : _Inherited(__tag, __a, __elements...) { } template<typename _Alloc, bool _NotEmpty = (sizeof...(_Elements) >= 1), @@ -1570,7 +1570,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX20_CONSTEXPR explicit tuple(allocator_arg_t __tag, const _Alloc& __a, - const _Elements&... __elements) + const __type_identity_t<_Elements>&... __elements) : _Inherited(__tag, __a, __elements...) { } template<typename _Alloc, typename... _UElements, diff --git a/libstdc++-v3/testsuite/20_util/tuple/cons/121771.cc b/libstdc++-v3/testsuite/20_util/tuple/cons/121771.cc new file mode 100644 index 000000000000..40bb37aced4f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/cons/121771.cc @@ -0,0 +1,45 @@ +// { dg-do compile { target c++17 } } + +// Bug 121771 - std::tuple CTAD fails for lvalue reference to function type + +#include <tuple> +#include <memory> + +void func(); + +std::tuple t(func); +std::tuple<void (*)()>& r = t; + +struct Explicit { + Explicit(); + explicit Explicit(const Explicit&); +} ex; + +std::tuple t2(func, 1); +std::tuple<void (*)(), int>& r2 = t2; + +std::tuple t2x(ex, func); +std::tuple<Explicit, void (*)()>& r2x = t2x; + +std::tuple t3(1, func, 3); +std::tuple<int, void (*)(), int>& r3 = t3; + +std::tuple t3x(ex, 2, func); +std::tuple<Explicit, int, void (*)()>& r3x = t3x; + +std::allocator<int> alloc; + +std::tuple ta(std::allocator_arg, alloc, func); +std::tuple<void (*)()>& ra = ta; + +std::tuple ta2(std::allocator_arg, alloc, func, 1); +std::tuple<void (*)(), int>& ra2 = ta2; + +std::tuple ta2x(std::allocator_arg, alloc, ex, func); +std::tuple<Explicit, void (*)()>& ra2x = ta2x; + +std::tuple ta3(std::allocator_arg, alloc, 1, func, 3); +std::tuple<int, void (*)(), int>& ra3 = ta3; + +std::tuple ta3x(std::allocator_arg, alloc, ex, 2, func); +std::tuple<Explicit, int, void (*)()>& ra3x = ta3x;
