How technical is too technical
Reading books on software engineering
Everyone agrees that reading books on software engineering is beneficial. But what should you read?
Focusing solely on books about software management or architecture can lead to being trapped in an ivory tower, disconnected from the hands-on work of coding itself.
What about taking the opposite approach?
As a professional C++ programmer, there are two key ways to improve your coding skills:
Experiment with APIs: Explore different APIs and test solutions to find the most elegant approach. Why aim for elegance? Because while there are often multiple ways to solve a problem, elegant solutions are typically more readable, maintainable, and intuitive.
Learn from others: Read books and articles that share the experiences of other developers. These resources often highlight various approaches, sparing you the time of discovering them all on your own.
What did i learn from modern c++?
I read Effective Modern C++ by Scott Meyers to learn the ins and outs of c++11 and 14.
Template and auto deduction
Template type deduction can sometimes ignore reference types, especially when dealing with complex, chained types. This behavior can lead to unexpected results.
With C++11, the most common instance of this is the auto
keyword, which operates similarly to template type deduction. You might have encountered cases where you expected a specific type, but the compiler deduced a different type, causing compatibility issues later. One key difference is the use of braces {}
, which indicate a std::initializer_list
.
The auto
keyword also introduces an interesting debate between conservative and progressive tool usage. Advocates of vi-style text editors often prefer explicit types, as they can infer the type directly from the source code. With auto
, this is no longer possible.
However, in modern IDEs, explicit type inference is unnecessary because type deduction is readily available via features like hover-over tooltips. This implicit deduction shifts focus to the variable’s purpose rather than its type—provided the variables are well-named.
Brace yourself
Having picked up my c++ skills after 2011, I used braced or universal initialization as much as I can. It has many advantages and the confusion that arises from the dual use of braces and parantheses is limited to a few cases: std::vector initialization
and templates.
Typedefs are for Nulls
Legacy C++ code, especially from older colleagues, often includes NULL
and typedefs
.
- Replace
NULL
withnullptr
.NULL
is essentially a compiler directive and less safe compared tonullptr
. - Strive for self-documenting code, which is easier to read and maintain. Avoid standard types when possible and define your own types for clarity.
The using
keyword offers a cleaner, more intuitive syntax compared to typedef
: It reads like an assignment and conceptually replaces typedef
, functioning to the class
keyword.
For constants, prefer constexpr
over #define
to ensure type safety and better integration with modern C++ features.
Keep it in order
Namespace pollution is a common issue in unstructured code. This applies to classic 2000-line functions, but also to modern tools which can lack proper scoping mechanisms, for example early versions of CMake.
In C++, scoped enums (enum class
) help address this problem by making enums behave more like classes. With enum class
, you must explicitly specify the scope to access a value, like telling the compiler which “drawer” to look in for the red_card
. For example:
enum class Color { Red, Green, Blue };
Color myColor = Color::Red;
This prevents naming conflicts and keeps the codebase cleaner.
const correctness
If you worked with classes you surely have come accross const correctness. New to me was the fact that the iterators can be const in C++14.
Being smart
By now, everyone is familiar with smart pointers and their advantages. However, I often encounter code that exclusively uses shared_ptr
. While this doesn’t usually impact performance, it significantly reduces readability.
My take aways: Use a standard type. If you get problems think about who is using the variable. If there is only owner and user of the variable use a unique pointer and move it. If you are not sure use a shared pointer. Always use make_shared/unique
. All other rules cover the 1 % of all use cases.
We are many
Concurrent programming is a big topic. My key take away is the prefered usage of std::async
instead of threads. Only keep the default launch policy in mind. If you have simultanous access to a member variable use std::atomic
.
And then it got too much
Much about the second half of the book is about the difference between lvalue and rvalue and when to move or forward something. I am rarely exposed with the more exotic cases in my work.
My next read
I hope my next book in this area will be far more useful : C++17 STL Cookbook by Jacek Galowicz.