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

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

第 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> 被嘲笑为 "not quite a container." LWG 96,最早的 LWG 问题之一,引发了争论。 17 年后的今天,vector<bool> 基本保持不变。

This paper 进入了一些具体示例,说明 vector<bool> 的行为与 vector 的所有其他实例有何不同,从而损害了通用代码。然而,同一篇论文详细讨论了如果实施得当,vector<bool> 可以拥有的非常好的性能属性。

总结vector<bool> 不是一个坏容器。它实际上非常有用。它只是有一个坏名字。

返回const_reference

detailed here 一样,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! 的断言将触发,但仅当 test 为 运行 且带有 vector<bool> 时才会触发。当 运行 与 vector<char>(或除 bool 之外的任何 vector 分配适当的非默认 T 时),测试通过。

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

在 libc++ 上,vector<char>vector<bool> 都通过了上述 test

费​​用是多少?

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

What is the motivation for this non-conformance?

为了在通用代码中为 libc++ 客户端提供更好的行为,也许在经过充分的现场测试之后,建议对未来的 C++ 标准进行此扩展,以改善整个 C++ 行业。

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

也就是说,事后看来 bitsetvector<bool>(应该重命名为 bit_vector -- 或其他),因为 arrayvector.无论我们是否将 bool 视为 vectorarray.

value_type,这个类比都应该成立

有多个 C++11 和 C++14 功能示例,这些功能最初是作为 libc++ 中的扩展出现的。这就是标准的演变方式。实际 证明 积极 现场经验具有很强的影响力。在更改现有规范(本应如此)时,标准制定者是一群保守派。猜测,即使您确定自己猜对了,对于发展国际公认的标准来说也是一种冒险的策略。