[gcc r16-7495] libstdc++: Make CTAD ignore tuple(const Types&...) constructor [PR121771]

Jonathan Wakely redi@gcc.gnu.org
Fri Feb 13 09:11:01 GMT 2026


https://gcc.gnu.org/g:7bafcc36172b572296731df951df4b0caa019b95

commit r16-7495-g7bafcc36172b572296731df951df4b0caa019b95
Author: Jonathan Wakely <jwakely@redhat.com>
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 <ville.voutilainen@gmail.com>

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;


More information about the Libstdc++-cvs mailing list