Recently, I was experiencing a strange crash that I traced to a piece of C++ code looking more or less like this:
template <class T> class container { public: std::vector<T> values_; T default_; T const& get() const { if (values_.empty()) return default_; return values.front(); } };
This was crashing when calling get()
, with a non-empty values_
member. It looks fairly innocent. And it ran in production for a couple of years already. So what changed?
I had, in fact, never instanciated this template with T = bool
before. And that was causing the crash, while still compiling without any errors. Now if you’re a little versed in the C++ standard library you might know that std::vector is a special snowflake indeed. In an effort to save space, and, I suspect, prove the usefulness of template specializations, it is not really a “normal” container holding bool values. Instead, it holds some type of integers and packs each pseudo-bool into one of their bits. The consequence is that the accessor functions like operator[]
, front()
and back()
cannot return a reference to a bool. Instead, they return a “proxy” object that supports assignment to and from a bool.
Back to the get()
function: it tries to return a reference to a bool. Of course, that bool doesn’t really exist except as a temporary, and so this results in a dangling reference that causes a segmentation fault when used.
I suspect there could have been a warning about a dangling reference somewhere there. I have seen clang-tidy especially report things like this (with a few false positives too), but it did not show up for me. To fix it, I am now just returning a bool
instead of a bool const&
for T = bool
. A special case in my case to work around a special case in std::vector
.