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


92

第23.3.7节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 ++一直未遵循此规范。

这种不符合的动机是什么?

Answers:


99

这种扩展的动机是通过一致的程序可以检测到的,因此可以通过不一致的方式进行检测,从而使其vector<bool>行为更像vector<char>参考(const和其他)。

介绍

自1998年以来,vector<bool>一直被嘲笑为“不是一个容器”。 LWG 96是最早的LWG问题之一,引发了这场辩论。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);
}

标准规范说,带标记的assert // Fires!会触发,但只有在test使用时才触发vector<bool>。当与运行vector<char>(或任何vector除了bool当一个适当的非默认T分配),测试通过。

libc ++实现试图使vector<bool>在通用代码中行为不同的负面影响最小化。为此,它要做的一件事就是创建 vector<T>::const_reference一个proxy-reference,就像指定的一样vector<T>::reference,只是不能通过它进行分配。也就是说,在libc ++上,vector<T>::const_reference它实际上是指向内的位的指针vector,而不是该位的副本。

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

要花多少钱?

缺点是此扩展名是可检测的,如问题所示。但是,实际上很少有程序关心此别名的确切类型,而更多的程序关心行为。

这种不符合的动机是什么?

为了使libc ++客户端在通用代码中有更好的行为,并可能在经过充分的现场测试之后,建议将此扩展扩展到将来的C ++标准,以改善整个C ++行业。

这样的建议可以采用新容器的形式(例如bit_vector),该容器具有与当今相同的API vector<bool>,但有一些升级,例如const_reference此处讨论的内容。随后弃用(并最终删除)该vector<bool>专业化。 bitset也可以在该部门进行一些升级,例如add const_reference和一组迭代器。

也就是说,在事后bitsetvector<bool>(应该被重新命名为bit_vector-或其他),为arrayvector。而类比应该我们是否在谈论是成立的,boolvalue_typevectorarray

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


1
问题:@EricNiebler 最近关于代理迭代器的提案草案是否/会以某种方式使libc ++扩展合法化并置于vector<bool>更一流的地位?
TemplateRex

备注:我宁愿有一个vector_bool<Alloc>array_bool<N>表示包装版本(包括随机访问迭代器代理遍历所有位)的vector<bool, Alloc>array<bool, N>。但是,bitset<N>(和它的表弟boost::dynamic_bitset<Alloc>)代表了不同的抽象:即的压缩版本std::set<int>。因此,我想拥有适当的双向迭代器(在1位而不是在所有位上迭代),bit_array<N>bit_vector<Alloc>希望成为bitset系列产品的继承者。您对此有何看法?
TemplateRex

5
我对代理迭代器的建议草案将成为vector<bool>一个符合std的随机访问容器。这不是vector<bool>一个好主意。:-)我同意霍华德。它应该被称为别的东西。
Eric Niebler

1
为什么没有办法选择退出libc ++扩展并获得严格符合的行为?(我什至不要求将一致性设置为默认值,这只是禁用libc ++扩展名以编写可移植代码的一种方法)。如您所知,过去我曾被libc ++元组扩展所咬,最近又被bitset :: const_reference扩展所咬。
gnzlbg 2015年

5
@gnzlbg:有限的经济和时间资源可用于libc ++的初始开发。随后,注定了该实现方式不能使每个用户都满意。在有可用资源的情况下,要进行工程设计权衡,以最大程度地增加满意的用户数量,使整个C ++社区受益。抱歉,您的经历。我注意到,您遇到的元组扩展现在在当前的C ++ 1z工作论文中。在这个问题上,您在不知不觉中做出了牺牲,以使许多人受益,而其中许多人欠您感激之情。
Howard Hinnant 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.