On Thu, 13 Mar 2025 at 21:29, Patrick Palka <ppa...@redhat.com> wrote: > > On Thu, 13 Mar 2025, Ville Voutilainen wrote: > > > On Thu, 13 Mar 2025 at 23:16, Ville Voutilainen > > <ville.voutilai...@gmail.com> wrote: > > > > > > On Thu, 13 Mar 2025 at 23:03, Patrick Palka <ppa...@redhat.com> wrote: > > > > + // Defined as a template to work around PR libstdc++/116440. > > > > + template<class...> > > > > + constexpr explicit(!__convertible<const _Elements&...>()) > > > > + tuple(const _Elements&... __elements) > > > > > > I don't understand how a constructor template declared like this can > > > ever be called. The template parameter pack > > > can't be provided or deduced, and can't have a default. So we're > > > effectively making this signature always lose > > > overload resolution to the one that takes a pack of _UElements&&. > > > > > > Which may be fine. I can't head-compile a test that would fail in that > > > case. If any of the incoming argument isn't one > > > of _Elements, that constructor wins overload resolution anyway. If the > > > incoming arguments are exactly _Elements, that > > > constructor does the same thing as this one. I think. > > > > Oh, never mind. The pack is just deduced as an empty pack. > > Yep that's my understanding, though I don't know where in the standard > this is specified, a quick Ctrl+F is failing me. > > I can use template<int = 0> or template<typename = void> if that's > preferred :)
I would prefer template<typename = void> to the empty pack, I think the default template argument makes it a little more obvious how that constructor can be called (I'm sure Ville won't be the only one to raise an eyebrow at that). Thanks for figuring this out, and noticing that that the template-ness of that constructor is what changed between C++17 and C++20. I think when I re-implemented it using concepts I assumed the template-ness was there for the _ImplicitCtor / _ExplicitCtor stuff, which is done using explicit(bool) in C++20. I wasn't looking at the tuple(const _Elements&...) constructor at all, because the errors all pointed to tuple(_UTypes&&...). Do we also want to constraint the tuple(const _Elements&...) constructor with requires sizeof...(_Elements) >= 1, which is present on the C++17 version?