类型特征是否应该能够处理std::vector < std::unique_ptr <int> >
这样的情况,并检测到它是不可复制构造的?
下面是https://ideone.com/gbcRUa上的一个示例(运行g++ 4.8.1)
#include <type_traits>
#include <vector>
#include <iostream>
#include <memory>
int main()
{
// This prints 1, implying that it's copy constructible, when it's clearly not
std::cout << std::is_copy_constructible< std::vector<std::unique_ptr<int> > >::value << std::endl;
return 0;
}
如果这是is_copy_constructible
的正确行为,那么有没有办法检测到复制构造的格式错误呢?
3条答案
按热度按时间4si2a6ki1#
这是因为
std::vector
的设计中存在缺陷。std::vector
定义了复制构造(即使它将无法编译),并依赖于std::vector
的用户在它将无法编译时不调用该方法。另一种设计是,如果
vector
中包含的类型没有复制构造函数,则SFINAE阻止方法的调用。然而,std::vector
是在现代SFINAE技术发展之前设计的。它可能会被重新安装到C的新迭代中,因为只有很少的代码会被破坏。不能说没有代码会被破坏,因为你可能有依赖于
std::is_copy_constructible< std::vector< no_copy_type > >
是std::true_type
这一事实的代码,或者等价的表达式,但这是一个相当奇怪的依赖。除了
std::vector
比能够解决这个问题的SFINAE技术更老的事实之外,用SFINAE来做这件事是相当麻烦的(因为SFINAE是一个麻烦的技术)。为C1y提出的新概念-lite可能会使它更干净,并且更有吸引力地包含在语言的新迭代中。当我有一个容器需要知道它所包含的对象是否可以被安全地复制、比较和排序时,我的解决方法是在一个自定义traits类上为
std::vector
专门化,然后在所包含的类型上依赖自定义traits类的值。这是一个拼凑的解决方案,而且相当麻烦。它给出了:
对于
<
和==
也是类似的。当我遇到更多的容器类型时,我可以添加更多的专门化,这些容器类型实际上应该将它们的属性向下转发到它们的数据,或者我可以编写一个更漂亮的SFINAE容器测试和特征,提取底层的值类型,并将问题分派给值类型上的测试。但是根据我的经验,我大多数时候都是在
std::vector
上完成这些测试。请注意,由于c++11 vector已经添加了“参与重载解析”规则,这是“do SFINAE”测试的标准说法。
vd2z7a6w2#
C++11标准的表49列出了类必须满足的
is_copy_constructable<T>::value
为真的条件,但遗憾的是,这些条件并不多:is_constructable<T, const T&>::value
就是true
因此,如果
std::vector<T>
有复制构造函数,它就通过了测试。qcbq4gxm3#
我想澄清一些被接受的答案所说的话(来自@Yakk - Adam Nevraumont的那个)。
如果
std::vector
没有正确地删除不可复制类型的复制构造函数,这不是因为设计缺陷或缺乏现代SFINAE技术,而是因为创建它的人希望能够示例化一个不完整类型的向量。容器既可以正确地SFINAE它们的特殊成员,也可以支持不完全类型。没有好的选择或错误的选择,两者都有各自的好处,有关这方面的更多细节,你可以查看这篇文章,其中去更深入:https://quuxplusone.github.io/blog/2020/02/05/vector-is-copyable-except-when-its-not/。
由于C++17
std::vector
需要在分配器允许的情况下(默认情况下允许),才能示例化不完整的类型:[矢量.概述]
如果分配器满足分配器完整性要求,则在示例化向量时可以使用不完整类型T.在引用向量得结果专用化得任何成员之前,T应该是完整得.
[分配器.要求.完整性]
如果X是类型T的分配器类,则无论T是否是完整类型,如果满足以下条件,则X还满足分配器完整性要求:
[默认.分配器]
默认分配器的所有专门化都满足分配器完整性要求([allocator.requirements.completeness])。