14

如果我们有一个具有不可复制值类型的容器,这样的容器类仍然定义了复制构造函数,只是它可能不会被调用。

using T = std::vector<std::unique_ptr<int>>;
std::cout << std::is_copy_constructible_v<T>; // prints out "1" (libstdc++)

这可能会导致“隐藏”问题,例如此处讨论的问题:Visual Studio 2017 是否需要显式移动构造函数声明?.

我的问题是标准库实现是否可以将此类复制构造函数定义为有条件地删除,即在不可复制值类型的情况下删除。这对我来说很有意义(至少在有 C++ 概念之前)。这样的实现会符合标准吗?

4

3 回答 3

10

自从vector获得不完整的类型支持以来,这在数学上是不可能的:

struct E {
    std::vector<E> e;
};

E是可复制的当且仅当std::vector<E>是可复制的,并且std::vector<E>是可复制的当且仅当E是可复制的。乌龟一路下来。

甚至在那之前,因为分配器construct可以在它认为合适的时候破坏构造函数参数,并且容器无法判断某些东西是否是“分配器可构造的”,有条件地删除复制构造函数需要一些认真的设计工作。不完整的类型支持只是把钉子钉在棺材里。

于 2018-11-08T18:37:19.677 回答
4

简短的回答:不。如果我们查看 std::vector 的当前规范(从 c++17 开始),我们有以下签名和描述:

vector(const vector& other);

复制构造函数。用其他内容的副本构造容器。如果没有提供 alloc,分配器就像调用 std::allocator_traits::select_on_container_copy_construction(other.get_allocator()) 一样。

复制构造函数具有通常的规范签名,并且描述未指定任何 SFINAE 条件,因此符合要求的实现不应施加更严格的要求,例如条件删除。vector<unique_ptr<T>>然而,如果尝试显式或隐式调用 ' 复制 ctor ,则会发生实例化错误,因为该描述暗示了逐元素复制。因此,vector<unique_ptr<T>>不满足CopyConstructible要求,这很像删除了复制构造函数。

据我所知,条件删除没有语法支持,但 SFINAE 条件和即将到来的约束可以实现选择性重载解决方案。我仍然强烈建议不要在特殊行动中使用这些。应该使用它们通常的规范签名来定义特殊操作。

于 2018-11-08T18:21:54.397 回答
3

正如 TC 所说,这甚至可能不可行,但如果是的话,我相信Conforming implementations下的[member.functions]p2部分不允许这样做:

对于 C++ 标准库中描述的非虚拟成员函数,实现可以声明一组不同的成员函数签名,前提是对从本文档中描述的声明集中选择重载的成员函数的任何调用都表现为如果选择了该重载。[注意:例如,一个实现可以添加具有默认值的参数,或者用两个或多个具有等效行为的成员函数替换具有默认参数的成员函数,或者为成员函数名称添加额外的签名。——尾注]

于 2018-11-08T18:49:41.687 回答