C++ Logo

std-discussion

Advanced search

Re: some containers always copy constructible according to type_traits

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Mon, 03 Oct 2022 10:27:24 +0100
On 3 October 2022 10:11:48 BST, "Lénárd Szolnoki via Std-Discussion" <std-discussion_at_[hidden]> wrote:
>Hi,
>
>On 2 October 2022 23:38:58 BST, Barry Revzin via Std-Discussion <std-discussion_at_[hidden]> wrote:
>>On Sun, Oct 2, 2022 at 5:25 PM Edward Catmur <ecatmur_at_[hidden]> wrote:
>>
>>>
>>>
>>> On Sun, 2 Oct 2022 at 22:58, Barry Revzin <barry.revzin_at_[hidden]> wrote:
>>>
>>>>
>>>>
>>>> On Sun, Oct 2, 2022 at 4:47 PM Edward Catmur via Std-Discussion <
>>>> std-discussion_at_[hidden]> wrote:
>>>>
>>>>> On Sun, 2 Oct 2022 at 22:00, Aleksander Maciej Miera via Std-Discussion <
>>>>> std-discussion_at_[hidden]> wrote:
>>>>>
>>>>>> Hello,
>>>>>>
>>>>>> While fiddling with template metaprogramming I managed to run into an
>>>>>> issue related to some std:: containers and type traits. As far as I
>>>>>> have
>>>>>> researched it, it is a known (although not obvious at first)
>>>>>> shortcoming.
>>>>>>
>>>>>> Consider the following code snippet:
>>>>>>
>>>>>> #include <type_traits>
>>>>>> #include <memory>
>>>>>> #include <vector>
>>>>>>
>>>>>> struct Copyable{};
>>>>>> using MoveOnly = std::unique_ptr<Copyable>;
>>>>>>
>>>>>> static_assert(!std::is_copy_constructible_v<std::vector<MoveOnly>>);
>>>>>>
>>>>>> (BTW, the vector is used here as an example, but the list and
>>>>>> forward_list also behave the same way for the same reason)
>>>>>>
>>>>>> Counter-intuitively, the static assert fails. This stems from the fact
>>>>>> that the vector's copy constructor is not a function template; thus it
>>>>>> cannot be SFINAEd (everything can be a verb if one tries hard enough
>>>>>> ;))
>>>>>> away, because it's always declared.
>>>>>>
>>>>>
>>>>> Since C++20, the copy constructor can be constrained:
>>>>>
>>>>> vector(vector const&) requires std::copyable<T>;
>>>>>
>>>>> I think this would solve your issue. This would require the containers
>>>>> general requirements to lift this requirement (on `X u(a)`, that `T` is
>>>>> Cpp17CopyInsertable, etc.) from a precondition to a constraint. There are
>>>>> probably quite a few clauses that would need to be considered in such an
>>>>> effort.
>>>>>
>>>>
>>>> The issue is that vector<T> supports T being incomplete. So if you did
>>>> that, then:
>>>>
>>>> struct Node;
>>>> vector<Node> nodes;
>>>>
>>>> Would instantiate the copy constructor which would error on Node being
>>>> incomplete. So you'd have to come up with a way for vector<Incomplete> to
>>>> still work.
>>>>
>>>
>>> Isn't that ill-formed already as it instantiates the default and allocator
>>> ctors? Or have I misunderstood [vector.overview]/4?
>>>
>>
>>Fine:
>>
>>struct Node {
>> vector<Node> nodes;
>>};
>
>Interestingly clang and gcc have different opinion on cases like this. You do have to break the circular dependency (Node is copyable if vector<Node> is copyable, and vice versa), but once you do that, clang is happy with it.
>
>https://godbolt.org/z/M51z57PGa
>
>I think gcc is unfortunately right here, but maybe it could be worked around by removing the actual copy constructor with requires, and creating a template "copy constructor" in its place to defer some instantiations.

Workaround:

https://godbolt.org/z/vqdj5vKTh

>Anyway, even if that is possible, the main culprit for lack of usability is the implicit circular dependency on constraints if you just let the copy operations to be defaulted.
>
>Cheers,
>Lénárd
>--
>Std-Discussion mailing list
>Std-Discussion_at_[hidden]
>https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion

Received on 2022-10-03 09:27:26