libstdc++: Work around C++20 tuple<tuple<any>> constraint recursion [PR116440]
The type tuple<tuple<any>> is clearly copy/move constructible, but for reasons that are not yet completely understood checking this triggers constraint recursion with our C++20 tuple implementation (but not the C++17 implementation). It turns out this recursion stems from considering the non-template tuple(const _Elements&) constructor during the copy/move constructibility check. Considering this constructor is ultimately redundant, since the defaulted copy/move constructors are better matches. GCC has a non-standard "perfect candidate" optimization[1] that causes overload resolution to shortcut considering template candidates if we find a (non-template) perfect candidate. So to work around this issue (and as a general compile-time optimization) this patch turns the problematic constructor into a template so that GCC doesn't consider it when checking for copy/move constructibility of this tuple type. Changing the template-ness of a constructor can affect overload resolution (since template-ness is a tiebreaker) so there's a risk this change could e.g. introduce overload resolution ambiguities. But the original C++17 implementation has long defined this constructor as a template (in order to constrain it etc), so doing the same thing in the C++20 mode should naturally be quite safe. The testcase still fails with Clang (in C++20 mode) since it doesn't implement said optimization. [1]: See r11-7287-g187d0d5871b1fa and https://isocpp.org/files/papers/P3606R0.html PR libstdc++/116440 libstdc++-v3/ChangeLog: * include/std/tuple (tuple::tuple(const _Elements&...)) [C++20]: Turn into a template. * testsuite/20_util/tuple/116440.C: New test. Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
This commit is contained in:
parent
ab4e9fd943
commit
6570fa6f26
2 changed files with 37 additions and 6 deletions
|
@ -966,12 +966,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
: _Inherited()
|
||||
{ }
|
||||
|
||||
constexpr explicit(!__convertible<const _Elements&...>())
|
||||
tuple(const _Elements&... __elements)
|
||||
noexcept(__nothrow_constructible<const _Elements&...>())
|
||||
requires (__constructible<const _Elements&...>())
|
||||
: _Inherited(__elements...)
|
||||
{ }
|
||||
// Defined as a template to work around PR libstdc++/116440.
|
||||
template<typename = void>
|
||||
constexpr explicit(!__convertible<const _Elements&...>())
|
||||
tuple(const _Elements&... __elements)
|
||||
noexcept(__nothrow_constructible<const _Elements&...>())
|
||||
requires (__constructible<const _Elements&...>())
|
||||
: _Inherited(__elements...)
|
||||
{ }
|
||||
|
||||
template<typename... _UTypes>
|
||||
requires (__disambiguating_constraint<_UTypes...>())
|
||||
|
|
29
libstdc++-v3/testsuite/20_util/tuple/116440.C
Normal file
29
libstdc++-v3/testsuite/20_util/tuple/116440.C
Normal file
|
@ -0,0 +1,29 @@
|
|||
// PR libstdc++/116440 - std::tuple<std::tuple<std::any>> does not compile
|
||||
// { dg-do compile { target c++17 } }
|
||||
|
||||
#include <any>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
template <typename T>
|
||||
using TupleTuple = std::tuple<std::tuple<T>>;
|
||||
|
||||
struct EmbedAny {
|
||||
std::any content;
|
||||
};
|
||||
|
||||
static_assert(std::is_copy_constructible<TupleTuple<EmbedAny>>::value);
|
||||
static_assert(std::is_move_constructible<TupleTuple<EmbedAny>>::value);
|
||||
|
||||
static_assert(std::is_copy_constructible<TupleTuple<std::any>>::value);
|
||||
static_assert(std::is_move_constructible<TupleTuple<std::any>>::value);
|
||||
|
||||
static_assert(std::is_constructible_v<std::any, TupleTuple<std::any>>);
|
||||
|
||||
struct EmbedAnyWithZeroSizeArray {
|
||||
void* pad[0];
|
||||
std::any content;
|
||||
};
|
||||
|
||||
static_assert(std::is_copy_constructible<TupleTuple<EmbedAnyWithZeroSizeArray>>::value);
|
||||
static_assert(std::is_move_constructible<TupleTuple<EmbedAnyWithZeroSizeArray>>::value);
|
Loading…
Add table
Reference in a new issue