【问题标题】:Can copy constructors of containers be defined as deleted for non-copyable value types?对于不可复制的值类型,容器的复制构造函数可以定义为删除吗?
【发布时间】:2019-04-12 02:33:53
【问题描述】:
如果我们有一个具有不可复制值类型的容器,这样的容器类仍然定义了复制构造函数,只是它可能不会被调用。
using T = std::vector<std::unique_ptr<int>>;
std::cout << std::is_copy_constructible_v<T>; // prints out "1" (libstdc++)
这可能会导致“隐藏”问题,例如此处讨论的问题:Does Visual Studio 2017 need an explicit move constructor declaration?。
我的问题是标准库实现是否可以将这样的复制构造函数定义为有条件地删除,即在不可复制的值类型的情况下删除。这对我来说很有意义(至少在有 C++ 概念之前)。这样的实现会符合标准吗?
【问题讨论】:
标签:
c++
containers
language-lawyer
copy-constructor
noncopyable
【解决方案1】:
自从vector 获得不完整的类型支持后,这在数学上是不可能的:
struct E {
std::vector<E> e;
};
E 是可复制的,如果 std::vector<E> 是可复制的,std::vector<E> 是可复制的,如果 E 是可复制的。海龟一路向下。
甚至在那之前,因为分配器的construct 可以在它认为合适的时候破坏构造函数参数,并且容器无法判断某些东西是否是“分配器可构造的”,有条件地删除复制构造函数需要一些严重的设计工作。不完整的类型支持只是把钉子钉在棺材里。
【解决方案2】:
简短的回答:不。
如果我们查看 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 条件和即将到来的约束可以实现选择性重载解决方案。我仍然强烈建议不要在特殊行动中使用这些。应该使用它们通常的规范签名来定义特殊操作。
【解决方案3】:
作为 T.C.说这甚至可能不可行,但如果是的话,我相信 Conforming implementations 下的 [member.functions]p2 部分不允许这样做:
对于 C++ 标准库中描述的非虚拟成员函数,实现可以声明一组不同的成员函数签名,前提是对成员函数的任何调用都会从本文描述的声明集中选择重载文档的行为就像选择了该重载一样。
[ 注意:例如,实现可以添加具有默认值的参数,或者将具有默认参数的成员函数替换为具有等效行为的两个或多个成员函数,或者为成员函数名称添加额外的签名。
—— 尾注
]