为什么 std::is_copy_constructible_v<std::vector<MoveOnlyType>> 为 true?

jac*_*bsa 22 c++ type-traits language-lawyer libc++ c++17

在我的 clang 和 libc++ 版本(near HEAD)中,这static_assert通过了:

static_assert(std::is_copy_constructible_v<std::vector<std::unique_ptr<int>>>)
Run Code Online (Sandbox Code Playgroud)

当然,如果您实际上尝试复制构造唯一指针的向量,它将无法编译:

../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
Run Code Online (Sandbox Code Playgroud)

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

use*_*522 28

std::vector和其他容器(除了std::array)被指定有一个复制构造函数。这并未指定为以元素类型是否可复制为条件。如果元素类型不可复制,则仅禁止复制构造函数定义的实例化。

std::is_copy_constructible_v因此容器上的结果将永远是true。无法测试定义的实例化是否具有类型特征的格式良好。

如果元素类型不可复制,则可以指定不声明复制构造函数或将其排除在重载解析之外。然而,这会带来一个权衡,这篇博文中详细解释了这一点:https: //quuxplusone.github.io/blog/2020/02/05/vector-is-copyable- except-when-its-不是/

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

struct X {
    std::vector<X> x;
};
Run Code Online (Sandbox Code Playgroud)

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

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