I’ve finally started to look into the new features in C++11 and I thought it would be useful to jot down the highlights, for myself or anyone else who’s curious. Since there’s a lot of ground to cover, I’m going to look at each item in its own post — this one covers automatic type inference and generalised constant expressions.
This is the 3rd of the 8 articles that currently make up the “C++11 Features” series.
It’s been awhile since my previous C++11 post but I’m still planning to finish the series — eventually! In any case, this post examines new facilities for the compiler to infer types from context, to allow more compact expressions of type-safe code, and also the ability to specify a function or constructor as a compile-time constant.
It’s often a little cumbersome to figure out the type of a value in C++,
especially when you’re dealing with a lot of templated and STL code. Even when
the types are obvious they can still be rather verbose to specify without
adding any particular clarity to the code. C++11 has addressed these issues by
adding a new decltype
keyword and also taking the venerable old auto
keyword from C, dusting it off and giving it a new lease of life.
Even as a C programmer you may not be familiar with the auto
keyword, which
specifies that a variable has automatic storage. This basically means “a
normal variable” since all variables are automatic by default unless explicitly
declared otherwise with the register
keyword, which I’ve only ever seen used
in kernel and device driver code (and rarely even then).
Since it’s essentially entirely unused these days, C++11 has removed the old
meaning of auto
and instead allowed it to be specifyed instead of a type
where the type can be ascertained from the initialiser. For example, the type
of both these variables is long int
in C++11:
long one;
auto two = 123L;
This may not appear particularly handy, but if you’re dealing with something like an opaque handle or context value returned from some third party library it’s quite nice not to have to be explicit about the type everywhere. Also, how many people enjoy writing this sort of thing:
for (std::map<std::string, std::string>::const_iterator ci = myStringMap.begin();
ci != myStringMap.end; ++ci) {
// ...
}
Surely much more pleasant without harming readability one iota:
for (auto ci = myStringMap.begin(); ci != myStringMap.end(); ++ci) {
// ...
}
Related to the new functionality of auto
, the decltype
keyword evaluates to
the type of the expression within it — this is, of course, something the
compiler must know but the programmer may find awkward to discover and often
doesn’t care about. So, you could declare a container iterator like this:
const std::vector<int> myVector;
decltype(myVector.begin()) iterator;
This is more useful in combination with auto
:
auto context = library::function(arg);
decltype(context) anotherContext;
Be aware, however, that auto
and decltype
can evaluate to different types
for the same argument:
const std::vector<int> myVector(1);
auto v1 = myVector[0]; // has type int
decltype(myVector[0]) v2 = 2; // has type const int&
Now, from type inference to the not-very-related-but-in-the-same-post-anyway topic of constant expressions. Among C++ programmers, unnecessary use of the C preprocessor is typically frowned upon, for a number of legitimate reasons that are beyond the scope of this post — the executive summary is that it makes it even easier than usual to shoot yourself in the foot.
In C, the preprocessor is typically used for constant values so the compile
stage doesn’t need to go to the overhead of allocating storage. In C++,
however, the const
keyword is typically used instead — this has the advantage
of including little niceties like type safety, and still allows the compiler to
make all the same optimisations as it otherwise would with the preprocessor.
There are a number of limitations of constant expressions in C++03, however,
such as the requirement that they be of essentially integral type (including
enumerations). Several of these limitations have been lifted in C++11 with the
introduction of the constexpr
keyword which specifies that its attached type
is a compile-time constant and can be assumed as such by compiler
optimisations. So, now non-integral constants are available:
constexpr double pi = 3.141592;
constexpr double twoPi = 2 * pi;
The constexpr
keyword can also be used with functions:
constexpr int factorial(int x)
{
return (x > 0 ? x * factorial(x-1) : 1);
}
void someOtherFunction()
{
int myArray[factorial(5)];
// ...
}
In C++03 this would have been invalid as the return value of a function was
never a constant expression. In C++11 the constexpr
keyword allows the
programmer to tell the compiler that the function’s return value will be
the same at both compile and runtime. Of course, there are a lot of
restrictions on what you can do in such a function — generally it must consist
of only a return
statement and it can only reference other constexpr
functions and values.
Still, despite all the restrictions, hopefully this might discourage people from engaging in template metaprogramming which is, in general, obfuscatory obscurantism of the highest order.