CPP 11 and 14 - key changes
If you’ve been on the Python bandwagon lately, you have likely missed out on the amazing updates that C++ has had in 2011 and 2014. Another update is just around the corner in 2017. While I gear up for a new specification release next year, here is a list quick review of the main features of C++11 and C++14.
Commonly usable changes
These changes improve readability of the source code within a single function. For example, better strings, concise constructs, etc.
Brief declarations
Iterators in C++98 were long and wordy. This changes to a short notation using the auto
keyword.
vector<int>::const_iterator it = ...; // C++98
auto it = ...; // C++11
The type is inferred automatically from the return type on the right side.
Vectors and arrays
Vectors in C++ have always had the begin
and end
member functions. Now they’re no longer bound to a particular object and can operate on STL containers and arrays.
// C++11
vector<int> v = {-5, 10, 30, -50, 300};
auto first3 = find(begin(v), end(v), 3);
// These also work on arrays
const char names[] = ["Bill", "Mark", "Eric", "Tim", "Jeff"];
auto first4 = find_if(begin(names), end(names), ...);
C++14 adds a bunch of new variants:
// C++14
cbegin(v); cend(v); // Const iterator
rbegin(v); rend(v); // Reverse iterator
crbegin(v); crend(v); // const reverse iterator
Range based loops
C++11 introduces the idea of range based for loops.
// Convenient range based for loops
for(auto &e : names) {
// ...
}
for(auto &elem : {1, 2, 3, 4, 5}) {
// ...
}
// Does not work
for(auto e2 : iterator) {
}
While we’re discussing iterators, there’s a new keyword nullptr
to denote null pointers. NULL
is part of the library and not a language feature. Use of 0 often caused confusion. The nullptr
keyword makes the intentions clear.
The » issue
If you’ve worked with OpenCV, you’re familiar with the » issue.
vector<vector<int> > v; // C++98 Need to put an extra space between the two >
vector<vector<int>> v; // C++11 - just works fine (like it should)
Static asserts
Asserts are great for runtime checks but you couldn’t do compile time asserts. This is now possible with static_assert
.
assert(5 > 2); // Runtime assert
// Compile time assert
static_assert(sizeof(int) == 4, "Unsupported architecture");
The assertion is run during compilation. If the static assert fails, the compilation continues (and looks for other errors).
Strings
C++11 comes with support for UTF strings. I’m sure some folks find this very useful.
string s1 = u8"This is a UTF-8 string";
const char16_t *s2 = u"This is a UTF-16 string";
const char32_t *s3 = U"This is a UTF-32 string";
// Raw strings stard with R"( and end with ")
string raw_1 = R"(This is a very " \badly quoted string)";
// If you want to use the characters ") in your string, create
// a tag for: starts at R"xyz( and ends at )xyz"
string raw_2 = R"xyz(What if I wanted to use)xyz";
long long
is now available. alignas
and alignof
are available. There are hooks into the garbage collector.
Lambdas
With lambdas, you can write much cleaner code. The return type is inferred and the logic is closer to the usage.
int main() {
auto ... = find_if(begin(v), end(v), [](int n) { return n > 0; });
}
A lambda internally creates a new class (who’s type you never find out unless you use typeid
). Most of the code in this new class is optimized away but it leads to a problem. You do not have access to variables outside of the lambda.
...
int x = 5;
int y = 10;
auto lambda1 = [](int n) { n + x; }; // x is undefined inside the lambda
auto lambda2 = [x](int n) { n + x; }; // x is passed in by value
auto lambda3 = [&x](int n) { ... }; // x is passed in by reference
auto lambda4 = [=](int n) { ... }; // All locals are passed (not data members)
auto lambda4 = [&](int n) { ... }; // All locals are passed by ref
auto lambda5 = [=, &y](int n) { ... }; // All passed by val, except y
auto lambda6 = [&, =y](int n) { ... }; // All passed by ref, except x
This is called capturing variables inside a lambda. You can only capture local variables - not data members of a class. You can, however, capture the this
local variable.
C++98 does not allow you to define functions inside a function (you can define a class inside a function though). This restriction is somewhat removed with lambdas:
auto lambda2 = [x](int n) { ... }; // x is passed in by value
lambda2(4); // lambda2 defined inside a function
C++14 introduces generic lambdas as well. This makes it templatized. I won’t even dare to talk about this craziness.
// C++14
[](const auto &p1, const auto &p2) { ... }
Functions
Inferring return type
You can use the auto
keyword to infer the return type of a function. In C++11, you need to provide an operation on template types using decltype
.
// C++11
template<typename T, typename U>
auto product(const T &t, const U &u) -> decltype(t*u) {
// ...
}
The decltype
is required to infer the type of the product of T and U (which might be different for each pair of classes T and U). C++14 makes it even more brief:
// C++14
template<typename T, typename U>
auto product(const T& t, const U& u) {
return t*u;
}
Here, the type is inferred from the return
statement directly. In case of multiple return statements, the compiler expects consistent return types.
The
auto
keyword uses the template type deduction mechanism while thedecltype
keyword uses the (new) decltype type deduction mechanism.
Attributes
You can decorate functions. [[noreturn]]
allows you to flag functions that do not return back to the caller.
[[noreturn]]
void something() {
throw "error";
}
For a more complete list of attributes, look at the GCC page or the Clang page.
Dynamic exception specification
The noexcept
operator tells the programmer and the compiler that a particular function will never throw an exception. Things like constructors, assignments, etc might benefit from this.
class Base {
Base() noexcept;
Base(int i) noexcept;
}
This operator also enables the compiler to do specific optimizations - just like the const
at the end of a function.
Explicit override flagging
Virtual functions need to be overridden. But sometimes, users of your class may not know that.
// C++11
class Base {
void sum(int) final; // Can't override this anymore
virtual int average(); // Can be overridden
virtual void print() const; // Can be overridden
}
class Derived : public Base {
void sum(int); // Error!
int average() override; // Okay
void print(); // Compiled but whoops, no const.
// Make sure you remember to use `override`
}
These keywords are contextual. They do their job only when they appear at the expected places. So your code containing the variable called override is going to be fine.
Constructors and data-member initialization
This was a given in languages like Python but not in C++98.
// C++11
class Animal {
Animal() : Animal(4) { }
Animal(int legs) : Animal(legs, 0) { }
Animal(int l, int h) : legs(l), hands(h) { validate(); }
private:
int legs = 0; // Class level defaults
int hands = 0;
}
During code generation, the constructor is given priority to assign default values. If the constructor does not assign a variable, the class-level defaults are used.
It is possible to inherit constructors into a class as well.
// C++11
class Cat : public Animal {
using Animal::Animal; // Was illegal until C++11
// Now you have constructors
// Cat(), Cat(4), Cat(4, 0)
}
The new standard also allows you to initialize data-members (even ones that aren’t the usual int
based):
class Base {
int id;
complex<double> count = 1000;
int a[9] = {1, 2, 3, 4, 5};
}
You can initialize an array in the class declaration but you’ll need to specify the size of the array.
Explicit conversion operator
Sometimes, the compiler automatically does type conversion if possible. This can cause hard-to-find bugs in code. The explicit
keyword forces an explicit cast.
// C++98
class Fraction {
operator double() const; // The compiler can sometimes convert it implicitly
}
Fraction f;
double d = f; // Implicit conversion
// C++11
class Fraction {
explicit operator double() const; // Requires (double)fraction to work.
}
Fraction f;
double d = f; // Fails
double dc = (double)f; // Compiles!
What’s next?
I still haven’t talked about a bunch of things - templates, move semantics, RTTI and pointers. Maybe next time?
Time Tracking with Toggl and Tasker 2015: Year in review