为什么 std::is_copy_constructible_v<std::vector<MoveOnlyType>> 是真的?

Why is std::is_copy_constructible_v<std::vector<MoveOnlyType>> true?

在我的 clang 和 libc++ 版本中(接近 HEAD),这个 static_assert 通过:

static_assert(std::is_copy_constructible_v<std::vector<std::unique_ptr<int>>>)

当然,如果你真的尝试复制构造一个唯一指针的向量,它会编译失败:

../include/c++/v1/__memory/allocator.h:151:28: error: call to implicitly-deleted copy constructor of 'std::unique_ptr<int>'
        ::new ((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
[...]
note: in instantiation of member function 'std::vector<std::unique_ptr<int>>::vector' requested here
    const std::vector<std::unique_ptr<int>> bar(foo);
                                            ^
../include/c++/v1/__memory/unique_ptr.h:215:3: note: copy constructor is implicitly deleted because 'unique_ptr<int>' has a user-declared move constructor
  unique_ptr(unique_ptr&& __u) _NOEXCEPT

我假设这种情况是因为 std::vector<T> 实现在 T 不可复制构造时不使用 SFINAE 来禁用复制构造函数。但为什么不呢?标准中是否有说明必须以这种方式工作?这很不幸,因为这意味着我自己关于复制可构造性的 SFINAE 并没有围绕向量做正确的事情。

std::vector等容器(std::array除外)都指定了拷贝构造函数。这未指定以元素类型是否可复制为条件。如果元素类型不可复制,则仅禁止实例化复制构造函数的定义。

因此,容器上的 std::is_copy_constructible_v 将始终为 true。无法测试定义的实例化是否 well-formed 具有类型特征。

如果元素类型不可复制,则可以指定不声明复制构造函数或将其排除在重载决策之外。但是,这会附带一个 trade-off,在这个博客 post 中有详细解释:https://quuxplusone.github.io/blog/2020/02/05/vector-is-copyable-except-when-its-not/.

简而言之,如果我们希望能够使用类型不完整的容器,例如递归地喜欢

struct X {
    std::vector<X> x;
};

那么实例化容器class时,我们无法判断X是否可复制。因此复制构造函数的声明不能依赖于这个 属性.

自 C++17 起,标准要求 std::vectorstd::liststd::forward_list,但其他容器不需要,以像这样处理不完整的类型。