https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94062
--- Comment #12 from m.cencora at gmail dot com --- (In reply to Jonathan Wakely from comment #11) > > Standard says that if: > > std::is_constructible_v<Bar, Foo&&> > > then > > std::is_constructible_v<std::tuple<Bar>, Foo&&> > > Where does it say that? > > The fact a constructor participates in overload resolution doesn't mean it > has to be well-formed for all template arguments. Indirectly from following paragraph 20.5.3.1 in latest C++ draft: template<class... UTypes> constexpr explicit(see below) tuple(UTypes&&... u); 11 # Constraints: sizeof...(Types) equals sizeof...(UTypes) and sizeof...(Types)≥1 and is_constructible_v<Ti, Ui> is true for all i. 12 # Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u). 13 # Remarks: The expression inside explicit is equivalent to: !conjunction_v<is_convertible<UTypes, Types>...> So unless I am missing something, I see no escape hatch for making such constructor ill-formed for some types. > > > So I think this is not an instance of PR 82113, but an unfortunate > > consequence of how tuple is implemented in libstdc++. If tuple elements were > > not stored as base classes, but as members than elision is mandatory and it > > would work. > > Members using [[no_unique_address]] have the same restrictions. > > > But I guess to fix this you would have to break ABI (or change C++ > > standard). > > It wouldn't be an ABI break to make this compile with our std::tuple, > because it never compiled previously. But I'm a little uncomfortable with > making the ABI of std::tuple depend on whether its elements are copy > constructible, rather than just on the elements' layout. > > That would mean that std::tuple<Bar> changes layout if you later add a move > constructor to Bar. Not sure how, because you can create std::tuple<Bar> already by using other constructors, e.g.: #include <tuple> struct Bar { Bar() = default; Bar(int i); Bar(const Bar&) = delete; }; struct ConvertibleToBar { operator Bar(); }; std::tuple<Bar> ok1() { return {}; } std::tuple<Bar> ok2() { return {0}; } std::tuple<Bar> fail1() { return {ConvertibleToBar{}}; }