为什么向量不是<bool> STL 容器?
Why isn't vector<bool> a STL container?
Scott Meyers的书《Effective STL: 50 Specific Methods to Better Your Use of the Standard Template Library》中的第18项说要避免vector <bool>
,因为它不是一个STL容器,而且它并不真正容纳bool
。
以下代码:
vector <bool> v;
bool *pb =&v[0];
不会编译,违反了 STL 容器的要求。
错误:
cannot convert 'std::vector<bool>::reference* {aka std::_Bit_reference*}' to 'bool*' in initialization
vector<T>::operator []
返回类型应该是T&
的,但为什么它是vector<bool>
的特例呢?
vector<bool>
真正由什么组成?
该项目进一步说:
deque<bool> v; // is a STL container and it really contains bools
这可以用作vector<bool>
的替代品吗?
谁能解释一下?
出于空间优化的原因,C++标准(早在 C++98 年(明确指出vector<bool>
是一个特殊的标准容器,其中每个布尔值只使用一位空间,而不是像普通布尔值那样使用一个字节(实现一种"动态位集"(。作为这种优化的交换,它不提供普通标准容器的所有功能和接口。
在这种情况下,由于您不能获取字节内位的地址,因此诸如operator[]
之类的东西不能返回bool&
而是返回一个允许操作相关特定位的代理对象。由于此代理对象不是bool&
,因此您不能将其地址分配给bool*
,就像在"普通"容器上进行此类运算符调用的结果一样。反过来,这意味着bool *pb =&v[0];
不是有效的代码。
另一方面,deque
没有调用任何此类专用化,因此每个布尔值需要一个字节,您可以从operator[]
获取值返回的地址。
最后请注意,MS 标准库实现(可以说(是次优的,因为它对 deque 使用较小的块大小,这意味着使用 deque 作为替代品并不总是正确的答案。
问题是vector<bool>
返回代理引用对象而不是真正的引用,因此C++98样式代码bool * p = &v[0];
无法编译。但是,如果operator&
还返回代理指针对象,则可以编译具有auto p = &v[0];
的现代 C++11。Howard Hinnant写了一篇博客文章,详细介绍了使用此类代理引用和指针时的算法改进。
斯科特·迈耶斯(Scott Meyers(在关于代理类的更有效C++中有一个很长的第30项。你可以走很长的路来模仿内置类型:对于任何给定的类型T
,一对代理(例如 reference_proxy<T>
和iterator_proxy<T>
(可以相互一致,因为reference_proxy<T>::operator&()
和iterator_proxy<T>::operator*()
是彼此的逆向。
但是,在某些时候,需要将代理对象映射回以表现得像T*
或T&
。对于迭代器代理,可以重载operator->()
并访问模板T
的接口,而无需重新实现所有功能。但是,对于引用代理,您需要重载operator.()
,这在当前C++中是不允许的(尽管Sebastian Redl在BoostCon 2013上提出了这样的建议(。您可以像引用代理中的 .get()
成员一样进行详细的解决方法,或者在引用中实现T
的所有接口(这是为vector<bool>::bit_reference
所做的(,但这要么会丢失内置语法,要么引入没有内置类型转换语义的用户定义转换(每个参数最多可以有一个用户定义的转换(。
TL;DR:没有vector<bool>
不是容器,因为标准需要真正的参考,但它的行为几乎像容器一样,至少在 C++11(自动(下比 C++98 更接近。
vector<bool>
包含压缩形式的布尔值,仅使用一位作为值(而不是 bool[] 数组的 8 位(。在 c++ 中不可能返回对位的引用,因此有一个特殊的帮助程序类型"位引用",它为您提供了内存中某个位的接口,并允许您使用标准运算符和强制转换。
许多人认为vector<bool>
专业化是一个错误。
在一篇论文"在 C++17 中弃用退化库部件">
有人提议重新考虑向量部分专业化。
布尔部分专业化的历史由来已久 std::向量不满足容器要求,并且在 特别是,它的迭代器不满足随机的要求 访问迭代器。之前弃用此容器的尝试是 被拒绝 C++11, N2204.
拒绝的原因之一是不清楚它会是什么 表示弃用模板的特定专用化。那 可以用谨慎的措辞来解决。更大的问题是 载体的(包装(专业化提供了一个重要的 标准库的客户真正寻求的优化,但是 将不再可用。我们不太可能 弃用标准的这一部分,直到更换设施 提出并接受,例如N2050。不幸的是,没有这样的 目前正在向图书馆演变提交的修订提案 工作组。
看看它是如何实现的。 STL 大量构建在模板上,因此标头确实包含它们所做的代码。
例如,在这里查看 stdc++ 实现。
同样有趣的是,即使不是符合STL标准的位向量是llvm::BitVector。
llvm::BitVector
的本质是一个名为 reference
的嵌套类和合适的运算符重载,以使BitVector
的行为类似于 vector
,但有一些限制。下面的代码是一个简化的界面,用于显示 BitVector 如何隐藏一个名为 reference
的类,以使真正的实现几乎像一个真正的布尔数组,而无需为每个值使用 1 个字节。
class BitVector {
public:
class reference {
reference &operator=(reference t);
reference& operator=(bool t);
operator bool() const;
};
reference operator[](unsigned Idx);
bool operator[](unsigned Idx) const;
};
这里的这段代码具有很好的属性:
BitVector b(10, false); // size 10, default false
BitVector::reference &x = b[5]; // that's what really happens
bool y = b[5]; // implicitly converted to bool
assert(b[5] == false); // converted to bool
assert(b[6] == b[7]); // bool operator==(const reference &, const reference &);
b[5] = true; // assignment on reference
assert(b[5] == true); // and actually it does work.
这段代码其实有一个缺陷,尝试运行:
std::for_each(&b[5], &b[6], some_func); // address of reference not an iterator
将不起作用,因为assert( (&b[5] - &b[3]) == (5 - 3) );
将失败(在llvm::BitVector
内(
这是非常简单的LLVM版本。 std::vector<bool>
里面也有工作迭代器。因此,调用for(auto i = b.begin(), e = b.end(); i != e; ++i)
将起作用。也std::vector<bool>::const_iterator
.
但是,std::vector<bool>
仍然存在限制,使其在某些情况下表现不同。
来自 http://www.cplusplus.com/reference/vector/vector-bool/
布尔值的向量 这是矢量的专用版本,用于 对于 bool 类型的元素,并针对空间进行优化。
它的行为类似于矢量的非专用版本,具有 以下更改:
存储
- 不一定是布尔值数组,但库实现可以优化存储,以便每个值
存储在单个位中。- 元素不是使用分配器对象构造的,而是直接在内部存储中的正确位上设置它们的值。
成员- 函数翻转和用于成员交换的新签名。
- 一种特殊的成员类型,引用,一个类,它访问容器内部存储中的各个位,其接口
模拟布尔引用。相反,成员类型const_reference为 一个普通的布尔。- 容器使用的指针和迭代器类型不一定既不是指针也不是符合迭代器,尽管它们
应模拟其大部分预期行为。这些更改为这种专业化提供了一个古怪的界面,并且 支持内存优化而不是处理(可能适合也可能不适合 您的需求(。在任何情况下,都无法实例化 直接用于布尔值的矢量的非专用模板。解决方法 避免此范围使用其他类型(字符、无符号字符(或 容器(如 deque(使用包装器类型或进一步专注于 特定的分配器类型。
Bitset 是一个为固定大小提供类似功能的类 位数组。
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 在C应用程序中运行C++(带有STL)函数
- 使用2个键的cpp-stl::优先级队列排序不正确
- 在STL容器中使用模板类
- EASTL矢量<向量<int>>连续的
- 用C++中的CPerson(类)类型的对象初始化STL矢量
- 将stl字符串缩小到小于15个字符的容量
- 在为LINUX创建共享库时,如何避免STL的私有/弱副本
- 检查函数返回类型是否与STL容器类型值相同
- STL算法函数在多个一维容器上的使用
- 在STL - C++中按成绩对学生列表进行排序?
- 为什么 STL 容器适配器堆栈中的 top 返回常量引用?
- λ可以适应STL吗?
- 为什么使用 NDK 不能存在不同的 stl 实现?
- 如果我真的真的想从 STL 容器继承,并且我继承构造函数并删除新运算符,会发生什么?
- 使用 char 分隔符解析C++中的字符串,但将可重复的字符保留为每个解析的子字符串 (C++ STL) 中的分隔符
- 在C++中迭代 STL 集时出现奇怪的问题<CStudent>
- 如何在 C++17 STL 并行算法中处理调度?
- 在学习数据结构之前对STL有一个了解是好的吗?