At CppCon 2014, Herb Sutter gave a talk presenting the essential idioms of modern C++ programming. Here is a short summary.
C++ is a complex language, admits Herb, though it must not be complex for every programmer. Herb suggests a view of C++ whereby only a tiny subset of all programmers should concern themselves with the most arcane aspects of the languages, e.g. when dealing with low-level or library code, while all others can just use it in some "default" way that just works.
First case in point is preferring the use of range-for
loops. Indeed, while you can write:
for (auto i = begin(c); i != end(c); ++i) { ... }
in modern C++, an easier way of doing the same is:
for (auto& e : c) { ... }
which also provides greater readability.
Pointers, references, new, and delete
Another strong suggestion of Herb's is not using owning *
, new
, or delete
, except when encapsulated inside the implementation of low-level data structures. What is to be preferred to that is unique_ptr
, or when you know you are going to share the object, shared_ptr
. So you can easily write:
auto p = make_unique<widget>(); auto q = make_shared<widget>();
By using make_unique
and make_shared
you do not even need bothering about delete.
However, non-owning *
and &
are still a great resource and their best use case is for passing parameters into functions. What Herb considers a real anti-pattern is using any kind of reference counted variable to pass parameters. This will only be the possible cause for performance problems and should be used only when you want to transfer the ownership of the wrapped object.
Prefer auto
to declare local variables
The auto
keyword is one of the biggest features of modern c++, according to Herb. auto
can be used to deduce a type, but is can also be used to specify a type and stick to it. In you consider the following statement:
auto i = v.begin();
this will correctly deduce the type to be used. This is much easier than the following and goes without much thinking:
vector::const_iterator i = v.begin();
which specifically requires thinking about using a const_iterator
.
Correctness is the biggest advantage of auto
, but it is also beneficial on other accounts:
-
Maintainability: it makes the code adapt to changes in, e.g., the return value of a function.
-
Performance: as a corollary of correct type deduction,
auto
also guarantees that no temporary objects are inadvertently created, e.g. when initializing an object. -
Usability: in special contexts, such as lambda,
auto
is an enabling factor. -
Typing convenience: it allows to save a few keystrokes.
There are though a few cases where you cannot use auto
, namely when you are dealing with C-arrays or with types that are not moveable or not cheaply moveable, e.g.:
auto lock = lock_guard<mutext> { m }; // error: not movable auto ai = atomic<int>{}; // error: not movable auto a = array<int,50>{}; // correct, but expensive
Parameter passing
A large part of the talk is devoted by Herb to discussing parameter passing. His main point here, is that C++98 defaults are still valid in modern C++ and represent a great starting point, with some additional possibilities offered by rvalue optimization. By way of an example, what you should do always is:
class employee { std::string name_; public: void set_name(const std::string& name) { name_ = name; } // standard C++98 convention void set_name(std::string&& name) noexcept { name_ = std::move(name); } // rvalue optimization };
On the other hand, Herb suggests to use passing by value mostly in constructors. In other contexts, passing by value, while allowing rvalue optimization, can exact a big performance toll when you pass in a named parameter (i.e., not a temporary object), which would be copied over.
Tuples
Herb's final suggestion regards the use of tuples to return multiple values:
tie(iter, success) = myset.insert("Hello"); if (success) do_something_with(iter);
Besides stressing the importance of defaults and idioms, Herb also suggests to avoid overthinking, which generally leads to solutions that are overly obfuscated. On all accounts, his recipe is sticking with what the language offers at the most basic level and then measure to look for areas where it might make sense getting your hands dirty to try and get better performances.