First, some rules of thumb:
Use std::unique_ptr
as a no-overhead smart pointer. You shouldn’t need to bother with raw pointers all that often. std::shared_ptr
is likewise unnecessary in most cases. A desire for shared ownership often betrays a lack of thought about ownership in the first place.
Use std::array
for static-length arrays and std::vector
for dynamic.
Use generic algorithms extensively, in particular:
<algorithm>
<numeric>
<iterator>
<functional>
Use auto
and decltype()
wherever they benefit readability. In particular, when you want to declare a thing, but of a type that you don’t care about such as an iterator or complex template type, use auto
. When you want to declare a thing in terms of the type of another thing, use decltype()
.
Make things type-safe when you can. When you have assertions that enforce invariants on a particular kind of thing, that logic can be centralised in a type. And this doesn’t necessarily make for any runtime overhead. It should also go without saying that C-style casts ((T)x
) should be avoided in favour of the more explicit (and searchable!) C++-style casts (e.g., static_cast
).
Finally, know how the rule of three:
- Destructor
- Copy constructor
- Assignment operator
Has become the rule of five with the addition of the move constructor and move assignment operator. And understand rvalue references in general and how to avoid copying.
C++ is a complex language, so it’s difficult to characterise how best to use all of it. But the practices of good C++ development haven’t changed fundamentally with C++11. You should still prefer memory-managed containers over manual memory management—smart pointers make it easy to efficiently do this.
I would say that modern C++ is indeed mostly free of manual memory management—the advantage to C++’s memory model is that it’s deterministic, not that it’s manual. Predictable deallocations make for more predictable performance.
As for a compiler, G++ and Clang are both competitive in terms of C++11 features, and rapidly catching up on their deficiencies. I don’t use Visual Studio, so I can speak neither for nor against it.
Finally, a note about std::for_each
: avoid it in general.
transform
, accumulate
, and erase
–remove_if
are good old functional map
, fold
, and filter
. But for_each
is more general, and therefore less meaningful—it doesn’t express any intent other than looping. Besides that, it’s used in the same situations as range-based for
, and is syntactically heavier, even when used point-free. Consider:
for (const auto i : container)
std::cout << i << '\n';
std::for_each(container.begin(), container.end(), [](int i) {
std::cout << i << '\n';
});
for (const auto i : container)
frobnicate(i);
std::for_each(container.begin(), container.end(), frobnicate);