运算符 |= 在 std::vector<bool> 上

Posted

技术标签:

【中文标题】运算符 |= 在 std::vector<bool> 上【英文标题】:operator |= on std::vector<bool> 【发布时间】:2015-03-05 16:35:23 【问题描述】:

以下代码无法编译

#include <vector>
int main()

  std::vector<bool> enable(10);
  enable[0] |= true;
  return 0;

给出错误

no match for ‘operator|=’ (operand types are ‘std::vector<bool>::reference aka std::_Bit_reference’ and ‘bool’)

在我的实际代码中,我有一个位字段,其中包含我想要|= 的值以及函数的结果。

有很多简单的方法可以表达相同的想法,但是有什么好的理由不提供这样的运算符吗?

【问题讨论】:

你仍然可以做enable[0] = enable[0] | ... 为什么不使用bitset @Borgleader 在我的真实代码中长度是动态的 @Borgleader:因为那也不直接支持这个操作。 @Amxx,也许是 Boost 的dynamic_bitset?它的引用类型确实有复合赋值重载。 【参考方案1】:

主要原因是std::vector&lt;bool&gt; 是特殊的,它的规范特别允许实现以最小化内存使用。

对于bool 以外的任何向量,引用类型实际上可以是真正的引用(即std::vector&lt;int&gt;::reference 实际上可以是int &amp;) - 通常直接引用向量本身的元素。因此,引用类型支持底层类型可以进行的所有操作是有意义的。这是因为vector&lt;int&gt; 在内部有效地管理int 的连续数组。除了bool之外的所有类型也是如此。

但是,为了最大限度地减少内存使用,std::vector&lt;bool&gt; 可能无法(实际上可能不会)在内部与bool 的实际数组一起使用。相反,它可能会使用一些打包的数据结构,例如内部的unsigned char 数组,其中每个unsigned char 是一个包含8 位的位域。因此,长度为 800 的 vector&lt;bool&gt; 实际上会管理 100 unsigned char 的数组,并且它消耗的内存将是 100 字节(假设没有过度分配)。如果vector&lt;bool&gt; 实际上包含800 bool 的数组,则其内存使用量至少为800 字节(因为根据定义,sizeof(bool) 必须至少为1)。

为了允许vector&lt;bool&gt; 的实现者进行这种内存优化,vector&lt;bool&gt;::operator[] 的返回类型(即std::vector&lt;bool&gt;::reference)不能简单地是bool &amp;。在内部,它可能包含对底层类型的引用(例如unsigned char)和跟踪它实际影响的位的信息。这将使所有 op= 运算符(+=-=|= 等)对底层类型进行一些昂贵的操作(例如位摆弄)。

std::vector&lt;bool&gt; 的设计者将面临选择

    指定std::vector&lt;bool&gt;::reference支持所有 op= 并不断听到来自 使用这些运算符的程序员

    不要支持那些 op= 和那些认为这样的事情没问题(“更简洁的代码”等)的程序员的抱怨,即使它们效率低下。

看来std::vector&lt;bool&gt; 的设计者选择了选项2。结果是std::vector&lt;bool&gt;::reference 支持的唯一赋值运算符是标准operator=()(操作数类型为reference,或类型为@ 987654358@) 不是任何 op=。这种选择的好处是,如果程序员试图做一些在实践中实际上是一个糟糕的选择,就会得到一个编译错误。

毕竟,尽管bool 支持所有 op=,但使用它们并没有太大的作用。例如,some_bool |= truesome_bool = true 具有相同的净效应。

【讨论】:

【参考方案2】:

你为什么不做以下事情?

enable[0] = enable[0] | true;

【讨论】:

这就是我正在做的事情,但仍然会发现 |= 运算符更清洁......(不是我喜欢那种运算符,例如使用^= true 切换布尔值) 您可以按照 David Schwartz 的建议重载运算符,但我认为对于这样一个基本操作,这已经足够了。您的来电。 我也不认为值得添加一个运算符(特别是如果它写得不好并且包含分支)。然而对我来说,像|= 这样的操作员的主要观点是不必使用变量,从而降低了提示的风险 如果你坚持要引入新的算子,大卫施瓦茨的答案就是解决你的问题。 @erip: enable[0] 在逻辑上表示单个真/假值。 (它实际上是复杂的等等等等,但它代表单个真/假值)【参考方案3】:

你应该可以很容易地自己制作一个。比如:

std::vector<bool>::reference& operator |= (std::vector<bool>::reference& a, bool b)

    if (b)
       a = true;
    return a;

或者,std::bitset 很合适。

【讨论】:

【参考方案4】:

简短而甜蜜的回答:应该避免std::vector&lt;bool&gt;。请改用vector&lt;wchar&gt;。你实际上得到了一个容器,其中 bool 被打包成位,它给出了与其他向量不同的行为、慢代码并且无论如何没人关心内存。我想现在已经没有人喜欢这个了,但是倒转时钟会破坏太多代码......

【讨论】:

以上是关于运算符 |= 在 std::vector<bool> 上的主要内容,如果未能解决你的问题,请参考以下文章

声明变量以保存字符串列表时的内存分配

将 std::vector 分配给 std::valarray

为啥不能对 std::vector 使用前向声明?

为啥使用 std::vector 而不是 realloc? [关闭]

std::vector 段错误而不是抛出异常

使用带有 unique_ptr 向量的赋值运算符