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.
Do not use 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.
Providing constchar * to a function with reference will use the stack-local
memory instead of using a copy. If further, 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 &.
There is no type safety in reinterpret_cast usage, which is worse than memcpy.
However, the prevention of another temporary for portable code may be worth it.
Checking, if typename is a string is complex (even with C++17 extension).
auto does verbatim replacement of the return type, which can hide a stack-local copy or move.
Only use 'auto' for well-known iterators, status tuples, shared pointers etc, but never for
objects and object references.
Interoperating type safe with c strings is cumbersome.
Destructors of virtual classes should have lifetime annotation, unless
final class. Otherwise, debugging problems will be painful, because the
intended object lifetimes become ambiguous on reading the code. Consider
using clang -Wnon-virtual-dtor or recent msvc (~ year 2020).
Delete copy + move constructors for non-final classes with no pure-virtual methods.
Consider marking copy constructor as explicit and deleting copy assignment, if
copying class is expensive. Consider providing a "clone fn" as syntactic sugar.
Do not use std::vector<bool>, because it is a
bitset and has horrible naming. Accidental byte operations on
it like memcpy will be UB.
Streams do not enforce C abi and are overly complex for printing memory.
Further (no example here), they need to extensions to offer non-blocking reads,
writes and introspection.
Incomplete type can not be instantiated in template:
pull in all headers deps of headers
class forward declares may create circular dependencies
build each file individually
might be a circular dependency during template usage
Missing virtual destructor for non-final methods in classes is technically UB.