C++11/14 is a significant change from C++98/03, and features like move semantics take a while to get used to. Also, people tend to be quite conservative about adopting new features (especially if they look unfamiliar). It took us in the games industry a while to move to C++ from C. But here are what I consider the no-brainer, programming-in-the-small things to adopt. Extra safety and expressiveness with zero runtime impact and pretty much no potential for misuse (never say never, but you’d really have to go out of your way).
1. using
instead of typedef
Not only is it a whole two characters fewer to type, it’s easier to read in the common function pointer case, and you can use it for template aliases, which often saves a lot more verbosity in the rest of the code. No more typename
everywhere! Compare:
// old and busted: typedef
template
struct Foo
{
// I can't typedef Foo::type
// and I have to use typename everywhere
typedef T type;
};
// Typical usage: a function pointer
typedef void (*funcptr)(int);
// new hotness: using
template
struct Foo
{
using type = T;
};
// This pattern is used a lot in C++14 STL
template
using foo_t = typename Foo::type;
// Function pointer is easier to read
using funcptr = void (*)(int);
2. static_assert
Compile-time asserts; what’s not to like? Odds are you have a homegrown C++98 TMP version of this somewhere; now it’s part of the language. Extra safety for zero runtime cost.
3. nullptr
Again, extra safety for zero cost. Ditch your zeros and NULLs, and you can safely have functions overloaded on pointer and integral types.
4. scoped enum
s (enum class
)
The compiler can help prevent accidental confusion between types, and the enum
values don’t leak any more. Hurrah! (It’s like Haskell’s newtype
for integers!)
5. forward declarations of enum
s, underlying type for enum
s
This works on (new) scoped and (old) unscoped enum
s alike. You don’t have to put in fake bit-width values to force the size of an enum
any more, and you can hide the values separately from the declaration, so you don’t need to recompile everything when you add a value.
6. override
When (for example) the class you’re deriving from changes upstream, and now you’re accidentally not overriding a member function you thought you were… you’d really like to know that. With override
, a bug that might take you a couple of hours to track down becomes a trivial-to-fix compile error. (Often mentioned in the same breath as override
is final
, and it’s fine, but much less usefully applicable.)
Now, there are also a couple of things to stop using.
1. rand()
Stop using rand()
. Really, it’s bad. Deep down, we always knew it was “bad” but maybe we convinced ourselves it was OK for “small”/”quick-and-dirty” tasks. It isn’t. And now, it’s not any easier or faster than just doing the Right Thing with <random>
. STL explains it all in rand() considered harmful.
2. bind
Lambdas supersede bind
in every way. They’re more straightforward to write, easier to read, as powerful and at least as efficient if not more so, and just… you know, not so weird. And if you think lambdas take some getting used to, well, I don’t think I ever got used to bind
…