“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:
featuresreturns a temporarystd::vector<bool>object (let’s call it temp), which is specified to represent itsbools in packed form, one bit perbool.operator[]is invokes on temp, and returns astd::vector<bool>::referenceobject (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 51autodeducesstd::vector<bool>::referenceas the tpe ofhighPriority, and bindhighPriorityto a copy of thisstd::vector<bool>::referenceobject- at the end of the statement,
tempis destroyed. Therefore, as a copy,highPrioritycontains 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
Tisbool,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&. ↩︎