为什么libc++的vector<bool>::const_reference不是bool?

Why is libc++'s vector<bool>::const_reference not bool?

本文关键字:bool reference 不是 const libc++ lt 为什么 gt vector      更新时间:2023-10-16

Section 23.3.7 class vector<bool> [vector]。Bool],第1段声明:

template <class Allocator> class vector<bool, Allocator> {
public:
    // types:
    typedef bool              const_reference;
    ...

然而,当使用libc++时,该程序无法编译:

#include <vector>
#include <type_traits>
int
main()
{
    static_assert(std::is_same<std::vector<bool>::const_reference, bool>{}, "?");
}

此外,我注意到c++标准在本规范中一直是一致的,一直追溯到c++ 98。而且我进一步指出,自从libc++首次引入以来,libc++一直没有遵循此规范。

不符合的动机是什么?

这个扩展的动机是使vector<bool>在引用(const和其他)方面的行为更像vector<char>,它可以被符合标准的程序检测到,因此不符合标准。

自1998年以来,vector<bool>一直被嘲笑为"不完全是容器"。第一批LWG议题之一的LWG 96引发了这场辩论。17年后的今天,vector<bool>基本保持不变。

本文讨论了vector<bool>的行为如何与vector的其他实例化不同,从而损害了泛型代码的一些具体示例。然而,同一篇论文详细讨论了vector<bool>如果正确实现可以拥有的非常好的性能属性。

总结: vector<bool>是一个不错的容器。它实际上很有用。它只是有个坏名声。

返回const_reference

如上所述,vector<bool>的缺点在于它在泛型代码中的行为与其他vector实例化的行为不同。下面是一个具体的例子:

#include <cassert>
#include <vector>
template <class T>
void
test(std::vector<T>& v)
{
    using const_ref = typename std::vector<T>::const_reference;
    const std::vector<T>& cv = v;
    const_ref cr = cv[0];
    assert(cr == cv[0]);
    v[0] = 1;
    assert(true == cv[0]);
    assert(cr == cv[0]);  // Fires!
}
int
main()
{
    std::vector<char> vc(1);
    test(vc);
    std::vector<bool> vb(1);
    test(vb);
}

标准规范说标记为// Fires!的断言将被触发,但只有当testvector<bool>一起运行时才会触发。当使用vector<char>运行时(或者在分配了适当的非默认T时,除了bool之外的任何vector),测试通过。

libc++实现试图将vector<bool>在泛型代码中的不同行为的负面影响最小化。为了实现这一点,它所做的一件事是使vector<T>::const_reference成为代理引用,就像指定的vector<T>::reference一样,只是您不能通过它进行分配。也就是说,在libc++中,vector<T>::const_reference实际上是指向vector内部的位的指针,而不是该位的副本。

在libc++上,上面的test同时通过vector<char>vector<bool>

代价是什么?

缺点是这个扩展是可检测的,如问题所示。然而,很少有程序真正关心别名的确切类型,更多的程序关心别名的行为。

不符合的动机是什么?

为了在泛型代码中给libc++客户端提供更好的行为,也许在经过充分的字段测试之后,为了整个c++行业的改进,建议在未来的c++标准中提出这个扩展。

这样的提议可能会以新容器(例如bit_vector)的形式出现,该容器具有与今天的vector<bool>相同的API,但会进行一些升级,例如这里讨论的const_reference。其次是弃用(并最终删除)vector<bool>专门化。bitset也可以在这方面进行一些升级,例如添加const_reference和一组迭代器。

也就是说,事后看来,bitsetvector<bool>(应该重命名为bit_vector——或者其他什么),就像arrayvector一样。无论我们是否将bool视为vectorarrayvalue_type,这个类比都应该成立。

有很多c++ 11和c++ 14特性的例子,最初是作为libc++的扩展。标准就是这样发展的。实际演示积极的现场经验具有很强的影响力。当涉及到改变现有规范时,标准人员是一群保守的人(他们应该这样做)。猜测,即使你确信你猜对了,对于发展一个国际公认的标准来说,也是一种冒险的策略。