c++ 向量上is_copy_constructible的误报< unique_ptr>

8hhllhi2  于 2022-11-27  发布在  其他
关注(0)|答案(3)|浏览(134)

类型特征是否应该能够处理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的正确行为,那么有没有办法检测到复制构造的格式错误呢?

4si2a6ki

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是一个麻烦的技术)。为C
1y提出的新概念-lite可能会使它更干净,并且更有吸引力地包含在语言的新迭代中。
当我有一个容器需要知道它所包含的对象是否可以被安全地复制、比较和排序时,我的解决方法是在一个自定义traits类上为std::vector专门化,然后在所包含的类型上依赖自定义traits类的值。这是一个拼凑的解决方案,而且相当麻烦。

template<template<typename>class test, typename T>
struct smart_test : test<T> {};
template<template<typename>class test, typename T, typename A>
struct smart_test<test, std::vector<T,A>> : smart_test<T> {};

它给出了:

template<typename T>
using smart_is_copy_constructible = smart_test< std::is_copy_constructible, T >;

对于<==也是类似的。当我遇到更多的容器类型时,我可以添加更多的专门化,这些容器类型实际上应该将它们的属性向下转发到它们的数据,或者我可以编写一个更漂亮的SFINAE容器测试和特征,提取底层的值类型,并将问题分派给值类型上的测试。
但是根据我的经验,我大多数时候都是在std::vector上完成这些测试。
请注意,由于c++11 vector已经添加了“参与重载解析”规则,这是“do SFINAE”测试的标准说法。

vd2z7a6w

vd2z7a6w2#

C++11标准的表49列出了类必须满足的is_copy_constructable<T>::value为真的条件,但遗憾的是,这些条件并不多:
is_constructable<T, const T&>::value就是true
因此,如果std::vector<T>有复制构造函数,它就通过了测试。

qcbq4gxm

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还满足分配器完整性要求:

  • X是完全类型,并且
  • allocator_traits的所有成员类型都是完全类型。

[默认.分配器]
默认分配器的所有专门化都满足分配器完整性要求([allocator.requirements.completeness])。

相关问题