“Invisible” proxy types can cause auto
to deduce the undesired type for an initializing expression, so we can adopt explicitly typed initializer idiom to force auto
to deduce what we want.
Some proxy classe are designed to be apparent to clients, such as std::shared_ptr
, and std::unique_ptr
. Other proxy classes are designed to at more or less invisibly, such as std::vector<bool>::reference
, and std::bitset::reference
.
For example, suppose we have a function that takes a Widget
and returns a std::vector<bool>
, where each bool indicates whether the Widget
offers a particular feature:
|
|
Now we want to check the value of bit 5, which indicates whether the Widget
has high priority:
|
|
However, if we change the explicit type for highPriority
with auto
:
|
|
The undefined behavior is caused by the fact that highPriority
contains dangling pointer. And here is what happened:
features
returns a temporarystd::vector<bool>
object (let’s call it temp), which is specified to represent itsbool
s in packed form, one bit perbool
.operator[]
is invokes on temp, and returns astd::vector<bool>::reference
object (an invisible proxy), which contains a pointer to a word in the data structure holding the bits that are managed bytemp
, plus the offset into that word corresponding to bit 51auto
deducesstd::vector<bool>::reference
as the tpe ofhighPriority
, and bindhighPriority
to a copy of thisstd::vector<bool>::reference
object- at the end of the statement,
temp
is destroyed. Therefore, as a copy,highPriority
contains a dangling pointer, leading to undefined behavior in the call toprocessWidget
.
Now is the time we adopt the explicitly typed initializer idiom: we declare a variable with auto
, but casting the initialization expression to the type we want auto
to deduce:
|
|
At this time, the std::vector<bool>::reference
object returned from std::vector<bool>::operator[]
executes the conversion to bool
it supports, and as part of that conversion, the still-valid pointer to temp (the std::vector<bool>
object returned from features
) is dereferenced, and the index 5 is then applied to the bits pointed to by the ponter, and the bool
value that emerges is used to initialize highPriority
- therefore, undefined behavior is avoided.
-
C++ forbids references to bits, so in the case where
T
isbool
,operator[]
forstd::vector<T>
cannot returnbool&
in packed form. That’s why the proxy is introduced here to make the return value act like abool&
. ↩︎