I’ve decided to create a sub-series of my Little Wonders posts to focus on C++. Just like their C# counterparts, these posts will focus on those features of the C++ language that can help improve code by making it easier to write and maintain. The index of the C# Little Wonders can be found here.
This has been a busy week with a rollout of some new website features here at my work, so I don’t have a big post for this week. But I wanted to write something up, and since lately I’ve been renewing my C++ skills in a separate project, it seemed like a good opportunity to start a C++ Little Wonders series. Most of my development work still tends to focus on C#, but it was great to get back into the saddle and renew my C++ knowledge.
Today I’m going to focus on a new feature in C++11 (formerly known as C++0x, which is a major move forward in the C++ language standard). While this small keyword can seem so trivial, I feel it is a big step forward in improving readability in C++ programs.
The auto keyword
If you’ve worked on C++ for a long time, you probably have some passing familiarity with the old auto keyword as one of those rarely used C++ keywords that was almost never used because it was the default.
That is, in the code below (before C++11):
1: int foo()
3: // automatic variables (allocated and deallocated on stack)
4: int x;
5: auto int y;
7: // static variables (retain their value across calls)
8: static int z;
10: return 0;
The variable x is assumed to be auto because that is the default, thus it is unnecessary to specify it explicitly as in the declaration of y below that. Basically, an auto variable is one that is allocated and de-allocated on the stack automatically. Contrast this to static variables, that are allocated statically and exist across the lifetime of the program.
Because auto was so rarely (if ever) used since it is the norm, they decided to remove it for this purpose and give it new meaning in C++11.
The new meaning of auto: implicit typing
Now, if your compiler supports C++ 11 (or at least a good subset of C++11 or 0x) you can take advantage of type inference in C++. For those of you from the C# world, this means that the auto keyword in C++ now behaves a lot like the var keyword in C#!
For example, many of us have had to declare those massive type declarations for an iterator before. Let’s say we have a std::map of std::string to int which will map names to ages:
1: std::map<std::string, int> myMap;
And then let’s say we want to find the age of a given person:
1: // Egad that's a long type...
2: std::map<std::string, int>::const_iterator pos = myMap.find(targetName);
Notice that big ugly type definition to declare variable pos? Sure, we could shorten this by creating a typedef of our specific map type if we wanted, but now with the auto keyword there’s no need:
1: // much shorter!
2: auto pos = myMap.find(targetName);
The auto now tells the compiler to determine what type pos should be based on what it’s being assigned to. This is not dynamic typing, it still determines the type as if it were explicitly declared and once declared that type cannot be changed. That is, this is invalid:
1: // x is type int
2: auto x = 42;
4: // can't assign string to int
5: x = "Hello";
Once the compiler determines x is type int it is exactly as if we typed int x = 42; instead, so don’t' confuse it with dynamic typing, it’s still very type-safe.
An interesting feature of the auto keyword is that you can modify the inferred type:
1: // declare method that returns int*
2: int* GetPointer();
4: // p1 is int*, auto inferred type is int
5: auto *p1 = GetPointer();
7: // ps is int*, auto inferred type is int*
8: auto p2 = GetPointer();
Notice in both of these cases, p1 and p2 are determined to be int* but in each case the inferred type was different. because we declared p1 as auto *p1 and GetPointer() returns int*, it inferred the type int was needed to complete the declaration.
In the second case, however, we declared p2 as auto p2 which means the inferred type was int*. Ultimately, this make p1 and p2 the same type, but which type is inferred makes a difference, if you are chaining multiple inferred declarations together. In these cases, the inferred type of each must match the first:
1: // Type inferred is int
2: // p1 is int*
3: // p2 is int
4: // p3 is int&
5: auto *p1 = GetPointer(), p2 = 42, &p3 = p2;
Note that this works because the inferred type was int
, if the inferred type was int*
1: // syntax error, p1 was inferred to be int* so p2 and p3 don't make sense
2: auto p1 = GetPointer(), p2 = 42, &p3 = p2;
You could also use const or static to modify the inferred type:
1: // inferred type is an int, theAnswer is a const int
2: const auto theAnswer = 42;
4: // inferred type is double, Pi is a static double
5: static auto Pi = 3.1415927;
Thus in the examples above it inferred the types int and double respectively, which were then modified to const and static.
The auto keyword has gotten new life in C++11 to allow you to infer the type of a variable from it’s initialization. This simple little keyword can be used to cut down large declarations for complex types into a much more readable form, where appropriate.