I’ve recently finished reading Effective Modern C++, which is the continuation of the “Effective C++” series for C++11/14. The book covered most of the newer features of modern C++ along with sufficient code examples to show their usage and applications. Overall, I’m pretty excited to see some of these features getting adopted in current and future C++ code bases. The motivation for a lot of these features naturally stemmed from the reasons of performance and efficiency, as is to be expected with anything C++ related.
Features such as auto, nullptr, constexpr, alias declarations, override, default/delete declarations, lambdas, smart pointers, and other features allow for smaller, cleaner, and in the case of some, less bug-prone code. At the same time, type traits, noexcept, rvalue/universal references and their move/forward semantics, allow for more efficient code generation and run-time performance gains.
However, some of these features are not without their pitfalls. For example, the auto keyword has some tricky pitfalls due to the rules of modern C++ type deduction.
int x = 123; auto y{ 123 }; std::cout << "type of x: " << typeid(x).name() << std::endl << "type of y " << typeid(y).name() << std::endl; |
the output of the following code is
type of x: int type of y class std::initializer_list < int >
and
std::vector boolVec = { true, false, false, true, true, false }; bool bSecondElem = boolVec[1]; auto bSecondElemAuto = boolVec[1]; std::cout << "type of bSecondElem: " << typeid(bSecondElem).name() << std::endl << "type of bSecondElemAuto " << typeid(bSecondElemAuto).name() << std::endl; |
outputs
type of bSecondElem: bool type of bSecondElemAuto class std::_Vb_reference < struct std::_Wrap_alloc> >
These were explained away by stating the different rules of auto type deduction versus template type dedication the beginning of the book, or the pitfalls of auto type deduction when dealing with classes such as std::vector < bool >, which save space by storing a bit per item and provide a reference to a proxy object when their operator[] is invoked. There are other notable edge cases, such as passing arguments through braced initializers to forwarding templates (Item 30, along with other issues), possible problems of dangling references from using default-capture lambdas (Item 31), and others. Even given these, I’m still excited to use these features (where appropriate) and see the benefits that modern C++ brings.