std::vector<bool>::reference

developed a templated class wrapping a std::vector<T>. All was fine until I tried to use it with T = bool. This is how I discovered that in the case of a std::vector<bool> the [] operator does not return a reference to bool but rather an object of type std::vector<bool>::reference as an r-value.

The following example illustrates the difference in the behavior of the [] operator for bool and a numeric type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <vector>

template<class T>
auto foo(T& d,int i){return d[i];}

template<class T>
auto& fun(T& d,int i){return d[i];}

int main(){

std::vector<bool> dbool(100) ;
std::vector<int>  dint(100) ;

foo(dbool,56) = true;
fun(dint,56) = 87;

//fun(dbool,56) = true;  ->does not compile
//foo(dint,56) = 87;     ->does not compile

dbool[24] = true;

}


It is an inconvenience, but i understand the logic, several bits are stored at the same memory address so a regular reference would not point to a single bit. The object returned is an accessor which includes the extra information enabling to select the correct bit, and it needs to be returned as r-value because it is a temporary (if it were not the case and accessors had a permanent existence, they would use much more memory than saved by packing the bools).

What I can't understand then is how the line dbool[24] = true; works if the left side is not an l-value?
Last edited on
An rvalue of a class type can be modified (can be assigned to) using its own member function.

For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

struct reference
{
    int& r ;
    reference& operator= ( int v ) { r = v ; return *this ; }
};

reference foo( int& v ) { return {v} ; }

int main() {

    int i = 8 ;
    foo(i) = 999 ;
    std::cout << i << '\n' ; // 999
}

http://coliru.stacked-crooked.com/a/4db89310468723cd
It is an inconvenience

You can say this instead:
1
2
template<class T>
decltype(auto) foo(T& d, int i) { return d[i]; }

auto and decltype(auto) are called placeholder types. Those types are deduced in a process called placeholder type deduction.

If auto is used as a placeholder type, the type is deduced using the rules about template argument deduction. If decltype(auto) is used, the rules about decltype are used instead.

In certain circumstances, decltype returns a reference type if the expression it is applied to has the correct value category. Our case is one such circumstance*.

In our case, the return type of foo<std::vector<int>> is an lvalue reference type, because placeholder type deduction applies decltype to d[i] where d is a std::vector<int>. This is an lvalue expression, so decltype(auto) is deduced as std::vector<int>::value_type &.

In contrast, the return type of foo<std::vector<bool>> is a value type, because placeholder type deduction applies decltype to d[i] where d is a std::vector<bool>. This is a prvalue expression, so decltype(auto) is deduced as std::vector<bool>::reference.

The result is that our function works fine in both cases.

1. This circumstance is deduction from an expression that is not an unparenthesized id-expression (name) or unparenthesized member-access expression. See:
https://en.cppreference.com/w/cpp/language/decltype
https://timsong-cpp.github.io/cppwp/n4659/dcl.type.simple#4
Last edited on
@JL Borges
very clear! (as usual!)
Thanks!.
Last edited on
@mbozzi

Thanks for your response. I wrote
It is an inconvenience
not because auto its doing its job but because std::vector<bool> breaks the logic of std::vector<T>. Moreover, the reason for this is not a property of the type bool but rather the specifics of the implementation of std::vector<bool> (It would have been possible to write std::vector<bool> in a manner consistent with std::vector<T>, it would just not be space efficient).

The implication is that the usage of std::vector<bool> is fundamentally different from that of std::vector<T> as illustrated in the following example.

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <vector>

int main()
{
    typedef int  T; //compiles
//    typedef bool T; //doesn't compiles
    std::vector<T> v(10);
    T& ref =v[5];
}


To me std::vector<bool> seems like an ugly duckling in the std::vector<T> family. It is perhaps useful, but its naming is unfortunate. In fact, this turns out to be an old controversy among C++ gurus and there have been proposals to remove std::vector<bool>. See:
http://www.gotw.ca/publications/N1185.pdf
Last edited on
Everyone seems to agree that std::vector<bool> was a mistake but it is probably too late to fix it now.
Topic archived. No new replies allowed.