From: Jonathan Wakely Date: Wed, 11 Feb 2026 22:39:52 +0000 (+0000) Subject: libstdc++: Make CTAD ignore tuple(const Types&...) constructor [PR121771] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7bafcc36172b572296731df951df4b0caa019b95;p=thirdparty%2Fgcc.git 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 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 --- diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 7f8cb7cbb1f..f7caa79cda0 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 constexpr explicit(!__convertible()) - tuple(const _Elements&... __elements) + tuple(const type_identity_t<_Elements>&... __elements) noexcept(__nothrow_constructible()) requires (__constructible()) : _Inherited(__elements...) @@ -1161,7 +1161,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr explicit(!__convertible()) tuple(allocator_arg_t __tag, const _Alloc& __a, - const _Elements&... __elements) + const type_identity_t<_Elements>&... __elements) requires (__constructible()) : _Inherited(__tag, __a, __elements...) { } @@ -1470,14 +1470,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template= 1), _ImplicitCtor<_NotEmpty, const _Elements&...> = true> constexpr - tuple(const _Elements&... __elements) + tuple(const __type_identity_t<_Elements>&... __elements) noexcept(__nothrow_constructible()) : _Inherited(__elements...) { } template= 1), _ExplicitCtor<_NotEmpty, const _Elements&...> = false> explicit constexpr - tuple(const _Elements&... __elements) + tuple(const __type_identity_t<_Elements>&... __elements) noexcept(__nothrow_constructible()) : _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= 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 +#include + +void func(); + +std::tuple t(func); +std::tuple& r = t; + +struct Explicit { + Explicit(); + explicit Explicit(const Explicit&); +} ex; + +std::tuple t2(func, 1); +std::tuple& r2 = t2; + +std::tuple t2x(ex, func); +std::tuple& r2x = t2x; + +std::tuple t3(1, func, 3); +std::tuple& r3 = t3; + +std::tuple t3x(ex, 2, func); +std::tuple& r3x = t3x; + +std::allocator alloc; + +std::tuple ta(std::allocator_arg, alloc, func); +std::tuple& ra = ta; + +std::tuple ta2(std::allocator_arg, alloc, func, 1); +std::tuple& ra2 = ta2; + +std::tuple ta2x(std::allocator_arg, alloc, ex, func); +std::tuple& ra2x = ta2x; + +std::tuple ta3(std::allocator_arg, alloc, 1, func, 3); +std::tuple& ra3 = ta3; + +std::tuple ta3x(std::allocator_arg, alloc, ex, 2, func); +std::tuple& ra3x = ta3x;