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{}};
}

Reply via email to