Here is a list of footguns taken from my notes I found worthwhile to write up to remind myself not to do the same mistake again or how to workaround various compiler problems. It consists of type problems, easy to make memory issues mostly around uninitialized memory and stack-local values, low quality library code, problems with virtual classes, template code problems and some compiler specific problems. For a more complete view on the edge cases of the language, consider taking a look at the C++ iceberg and preprocessor iceberg.
Update 2024-12-17: Since the writing of this article (2024-04-15), I did significantly extend my CI-tested sample code on C++14, C++17, C++20 and C++23 and do not plan to continue to list footguns. My main complains about C++ are
std::vector<bool>
are not getting fixed.std::variant
).std::string
or std::string_view
instead of working on user-provided information, for example provided via struct
with a fat pointer (pointer and length).The current trajectory of C++ indicates to me that none of this will be fixed, even though modules would soon allow C++ version 2. Personally I would prefer semantics more comparable to Zig, but so far Zig has no static analysis to reduce transition efforts from C++.
hashmap[key]
, use auto search_hashmap = hashmap.find();
and write via iterator or use emplace, because there is no check for the elements existence or (typically raw C) values of members can remain undefined after object creation due to implicit default constructor. c_str()
is used to emplace into a std::map
, this leads to UB due to usage of invalid memory once the stack local memory goes out of scope. In doubt, allocate a copy with std::string newstring = std::string(some_string)
Especially in std::map or other owned containers. Only if there is an explicit comment on the storage including handling of move and copy constructor, use (const) char*
as provided argument for (const) std::string &
.reinterpret_cast
usage, which is worse than memcpy. However, the prevention of another temporary for portable code may be worth it. auto
for well-known iterators, status tuples, shared pointers etc, but never for objects and object references.-Wnon-virtual-dtor
or recent msvc (ca. year 2020).std::vector<bool>
, because it is a dynamic bitset and has horrible naming. Accidental byte operations on it like memcpy will be UB.__STDC_VERSION__
is not necessary defined by C++ compilers.