如果我们有一个具有不可复制的值类型的容器,那么这样的容器类仍会定义 copy构造器,只是可能不会被调用。
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 ++概念为止)。这样的实现是否符合标准?
答案 0 :(得分:6)
从vector
获得不完整的类型支持以来,这在数学上是不可能的:
struct E {
std::vector<E> e;
};
E
是可复制的,如果std::vector<E>
是可复制的,而std::vector<E>
是可复制的,而E
是可复制的。乌龟一直向下。
即使在此之前,由于分配器的construct
可以在认为合适的情况下破坏构造函数参数,并且容器无法告知某些内容是否“分配器可构造”,因此有条件地删除复制构造函数将需要一些认真的设计工作。不完整的类型支撑只是将钉子钉在棺材上。
答案 1 :(得分:4)
一个简短的答案:不。 如果我们看一下std :: vector的当前规范(从c ++ 17开始),我们将具有以下签名和描述:
vector(const vector& other);
复制构造函数。用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条件和即将出现的Constraints可以实现选择性重载解决方案。我仍然强烈建议您不要在特殊操作上使用它们。特殊操作应使用其通常的规范签名进行定义。
答案 2 :(得分:3)
作为T.C.说这甚至可能不可行,但如果我认为符合实施情况下的[member.functions]p2部分不允许这样做:
对于C ++标准库中描述的非虚拟成员函数,实现可以声明一组不同的成员函数签名,前提是对该成员函数的任何调用都会从本文档中描述的声明集中选择一个重载。文档的行为就像选择了该重载一样。 [注:例如,实现可能添加具有默认值的参数,或用具有相同行为的两个或多个成员函数将成员函数替换为具有默认参数的成员函数,或为成员函数名称添加其他签名。 —注 ]