operator |= on std::vector<bool>
Asked Answered
Z

4

3

The following code doesn't compile

#include <vector>
int main()
{
  std::vector<bool> enable(10);
  enable[0] |= true;
  return 0;
}

giving the error

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

In my real life code I have a bit field with values I want to |= with the result of a function.

There are easy way to express the same idea, but is there any good reason for such an operator not to be available ?

Zippora answered 5/3, 2015 at 16:35 Comment(7)
You can still do enable[0] = enable[0] | ....Nauplius
Why not use a bitsetTeniers
@Teniers in my real code the length is dynamicZippora
@Borgleader: Because that doesn't directly support this operation either.Goose
@Amxx, Perhaps Boost's dynamic_bitset then? Its reference type does have compound assignment overloads.Gherardo
@DavidSchwartz Or simply enable[0] = true;Ettie
@DavidSchwartz : look at my full question ... In my real life code I have a bit field with values I want to |= with the result of a function.Zippora
F
7

The main reason would be that std::vector<bool> is special, and its specification specifically permits an implementation to minimise memory usage.

For vectors of anything other than bool, the reference type can actually be a true reference (i.e. std::vector<int>::reference can actually be an int &) - usually directly referencing an element of the vector itself. So it makes sense for the reference type to support all operations that the underlying type can. This works because vector<int> effectively manages a contiguous array of int internally. The same goes for all types other than bool.

However, to minimise memory usage, a std::vector<bool> may not (in fact probably will not) work internally with an actual array of bool. Instead it might use some packed data structure, such as an array of unsigned char internally, where each unsigned char is a bitfield containing 8 bits. So a vector<bool> of length 800 would actually manage an array of 100 unsigned char, and the memory it consumes would be 100 bytes (assuming no over-allocation). If the vector<bool> actually contained an array of 800 bool, its memory usage would be a minimum of 800 bytes (since sizeof(bool) must be at least 1, by definition).

To permit such memory optimisation by implementers of vector<bool>, the return type of vector<bool>::operator[] (i.e. std::vector<bool>::reference) cannot simply be a bool &. Internally, it would probably contain a reference to the underlying type (e.g. a unsigned char) and information to track what bit it actually affects. This would make all op= operators (+=, -=, |=, etc) somewhat expensive operations (e.g. bit fiddling) on the underlying type.

The designers of std::vector<bool> would then have faced a choice between

  1. specify that std::vector<bool>::reference support all the op= and hear continual complaints about runtime inefficiency from programmers who use those operators

  2. Don't support those op= and field complaints from programmers who think such things are okay ("cleaner code", etc) even though they will be inefficient.

It appears the designers of std::vector<bool> opted for option 2. A consequence is that the only assignment operators supported by std::vector<bool>::reference are the stock standard operator=() (with operands either of type reference, or of type bool) not any of the op=. The advantage of this choice is that programmers get a compilation error if trying to do something which is actually a poor choice in practice.

After all, although bool supports all the op= using them doesn't achieve much anyway. For example, some_bool |= true has the same net effect as some_bool = true.

Fortdefrance answered 5/3, 2015 at 17:58 Comment(1)
A
1

Why don't you just do the following?

enable[0] = enable[0] | true;
Anselmi answered 5/3, 2015 at 16:37 Comment(9)
Thats what I'm doing, but still a would find an |= operator to be cleaner ... (not that I love those kind of operator, such as doing ^= true for toggling a bool)Zippora
You could overload the operator as suggested by David Schwartz, but I think for such an elementary operation, this is good enough. Your call.Anselmi
I also don't think it's worth adding a operator (particularly if it is badly written and contains branches). Yet for me the main point of an operator like |= is not to have to the variable, hense reducing the risks of a tipoZippora
If you insist on introducing the new operator, David Schwartz's answer is the solution to your problem.Anselmi
@MooingDuck I assumed it wasn't going to be as simple as a single bool. Perhaps he wanted to do some bitwise operations on another std::vector<bool>.Anselmi
@erip: enable[0] logically represents a single true/false value. (it's actually complex blah blah blah, but it represents a single true/false value)Ettie
@MooingDuck I order to solve the juste do enable[i] = true argment ...here his what I basically want to fo for (const auto& v : voronoi) enable[i] |= v.intersects(wall[i]);Zippora
@Amxx: for (const auto& v : voronoi) if (v.intersects(wall[i])) enable[i] = true;Ettie
@MooingDuck v.intersects(wall[i]) is expensive, you want to use lazy evaluation to avoid computing it when enable[i] is already true. That's why I do for (const auto& v : voronoi) enable[i] = enable[i] || v.intersects(wall[i]);Zippora
N
1

You should be able to make one yourself pretty easily. Something like:

std::vector<bool>::reference& operator |= (std::vector<bool>::reference& a, bool b)
{
    if (b)
       a = true;
    return a;
}

Alternatively, std::bitset is a good fit.

Nauplius answered 5/3, 2015 at 16:38 Comment(0)
J
0

Short and sweet answer: std::vector<bool> should be avoided. Use vector<wchar> instead. You actually get a container back in which the bools are packed in bits, which gives different behaviour from other vectors, slow code and no-one cares a bout memory anyway. I guess by now no-one likes this anymore, but turning back the clock would break too much code...

Jacquelinjacqueline answered 6/3, 2015 at 7:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.