C++ 11 Auto: How to use and avoid abuse

My first encounter with the C++ 11 Auto keyword was in less than favorable circumstances. I had just moved to a new team, ventured into an area of hardcore C++ network and protocol development for Xbox, and struggling to understand some complicated game engine infrastructure like Halo and Gears of War. To top it all, I had not written C++ in over six years.

It was late friday afternoon and I encounter something like :

auto a = ConjureMagic();

The immediate question that cropped up in my mind was “what the heck is ‘a’ ?!? “ . The IDE helped a little bit because I could see the types if I hovered over the variable name in Visual Studio. However, hovering over the names every time I tried to find the type was very annoying. It disrupted my thought process of just reading the code and understanding the flow. But to my dismay, the code base was littered with it.

Fast forward 16 months and I now realize that my frustration with C++ 11 Auto keyword stemmed from the way it was used, and not the nature of the keyword itself. In fact, I’ve grown to be an advocate of using “auto” over the last year. Before I get into the reasons for being an “auto” convert , here’s a quick recap of what the “auto” keyword is.

Auto keyword simply tells the compiler to deduce the type of a declared variable from its initialization expression. It analogous to the “var” keyword in C# .  Here’s are four ways it has made my C++ development life easier:

#1 C++ 11 Auto makes defining complex or templatized data types a breeze

Auto cuts down on unnecessary typing of complex data types on the left hand side of assignment operator. For example, consider the two code snippets below used to initialize the  task scheduler for C++ concurrency runtime.

The first one uses the pre C++ 11 way of initializing variables (minus the fact it uses C++ 11 smart pointer):

std::shared_ptr<::pplx::default_scheduler_t> s_ambientScheduler = std::make_shared<::pplx::default_scheduler_t>();

Now consider the alternative with auto:

auto s_ambientScheduler = std::make_shared<::pplx::default_scheduler_t>();

Isn’t the second version much easier to read ? Here we’re already seeing what type s_ambientScheduler is from it’s initialization expression on the right – so no need to add verbosity to the code by mentioning the explicit type on the left. This is pretty much in line with DRY ( don't repeat yourself ) principle of software development.

#2 C++ 11 Auto makes STL iterator loops easier to write and comprehend

This is a big one. Prior to C++ 11, we needed to use fully qualified iterator types for looping through STL containers. The problem gets really complicated as we start using nested STL containers. For example, consider a nested STL map. It’s used to store the name of a student and the various grades he has received in different subjects.

std::map<std::wstring, std::map<std::wstring, int>> StudentGrades;
StudentGrades[L"Deb"][L"Physics"] = 96;
StudentGrades[L"Deb"][L"Chemistry"] = 92;
StudentGrades[L"Deb"][L"Math"] = 82;
StudentGrades[L"Vik"][L"Physics"] = 92;
StudentGrades[L"Vik"][L"Chemistry"] = 88;
StudentGrades[L"Vik"][L"Math"] = 91;

If we need to print out the grades, this is what the code would have looked like prior to C++ 11 (i.e. without using the auto keyword):

for (std::map<std::wstring, std::map<std::wstring, int>>::iterator outerMap_Iter = StudentGrades.begin(); outerMap_Iter != StudentGrades.end(); ++outerMap_Iter)
	//Print out the student name
	std::wcout << outerMap_Iter->first << std::endl;
	for (std::map<std::wstring, int>::iterator innerMap_Iter = outerMap_Iter->second.begin(); innerMap_Iter != outerMap_Iter->second.end(); ++innerMap_Iter)
		//Print the grades here
		std::wcout << innerMap_Iter->first << " : " << innerMap_Iter->second << std::endl;
	std::wcout << std::endl;

Does that make your head hurt? yeah – mine too !! The damn thing does not even fit on my laptop screen without showing the horizontal scroll-bars. But we had no alternatives before. Now we do – consider the consider the C++ 11 alternative with auto:

for (auto outerMap_Iter = StudentGrades.begin(); outerMap_Iter != StudentGrades.end(); ++outerMap_Iter) 
  //Print out the student name
  std::wcout << outerMap_Iter->first << std::endl;
  for (auto innerMap_Iter = outerMap_Iter->second.begin(); innerMap_Iter != outerMap_Iter->second.end(); ++innerMap_Iter)
	 //Print the grades here
	 std::wcout << innerMap_Iter->first << " : " << innerMap_Iter->second << std::endl;
  std::wcout << std::endl;

Here, instead of spelling out the iterator type , we let the compiler auto deduce it from the instantiation. And it almost fits on one screen  !

If you’re already hooked, it gets even better when combined with a range ranged for loop in C++ 11:

for (auto const &outer_iter : StudentGrades) 
  std::wcout << outer_iter.first << std::endl;
  for (auto const &inner_iter : outer_iter.second)
	 std::wcout << inner_iter.first << " : " << inner_iter.second << std::endl;

Now we’re talking ! Contrast this with our first implementation – just a glance at the two lines below shows the big picture:

Implementation #1 :

for (std::map<std::wstring, std::map<std::wstring, int>>::iterator outerMap_Iter = StudentGrades.begin(); outerMap_Iter != StudentGrades.end(); ++outerMap_Iter)

Implementation# 3:

for (auto const &outer_iter : StudentGrades)

Yes, implementation # 3 just saved you 111 keystrokes if you're writing this code and some scrolling and headache if you're reading this code !!!

#3 C++ 11 Auto comes in handy while storing lambda closures

C++ 11 lets you store lambda expressions in named variables in the same manner you name ordinary variables and functions. This enables you to use the lambda expression multiple times in different places without having to copy the code all the time. The auto keyword will take care to define func as a pointer to the lambda expression.

auto func_multiply = [](int a, int b) -> int { return a * b; };

This auto declaration defines a closure type named factorial that you can call later instead of typing the entire lambda expression(a closure type is in fact a compiler – generated function class) :

std::cout << func_multiply(2, 3) << std::endl;

At this point, you might ask what the alternative is ? Well, the alternative is to use a function object to store the lambda. Here’s an example:

std::function<int(int, int)> func_multiply2 = [](int a, int b) -> int { return a * b; };
std::cout << func_multiply2(2, 3) << std::endl;

See how ugly the left hand side looks ? I headache just graduated to a migranine 🙂 Jokes aside, using a function object instead of auto has two other ramifications – it can sometimes allocate heap memory to store the closure. This can lead to out-of-memory exceptions at certain times. Also, invoking a closure via std::function is slower than calling it via an auto declared object. For a more in depth discussion, you can check out item # 5 of Scott Meyer's "Effective Modern C++".

#4 C++ 11 Auto forces initialization of variables

	auto x1; // does not compile
	int x1; // ok for the compiler

Uninitialized variables in C++ are one of the worst sources of bugs in C++. We had a situation where our code was relying on an uninitialized variable as a multiplicative factor to determine the cache size on web front ends. When we deployed the solution to our staging/ test servers , it started causing random out of memory exceptions to the point where the front ends became unresponsive and had to be taken out of rotation. The problem was caused by the fact that the uninitialized variable sometimes held a very large value that was used to allocate server cache. To compound problems finding uninitialized variables, variable declared when running the program in a debugger are typically zeroed. This means your program may work fine every time when run in a debugger, but crash intermittently in release mode! So morale of the story – minimize the chances of getting into a situation where you might have uninitialized variables – using auto for your local variables helps with just that.

However, you need to be careful with C++ 11 Auto !


Okay, now that we've seen some of the ways auto can help us write consise and robust code, you might ask – "But Deb, what about your initial complain? ". Well, I still don't like two ways in which some folks use auto, namely:

#1 To Intercept the value of functions and then pass them as parameters to another function

 auto a = ConjureMagic();

I think in this situation we can do either of two things. Change the name of the function to something more descriptive ConjureMagicInteger(). Even better, just use the fully qualified type in this case.

 auto a = ConjureMagic() ; // worst
 auto a = ConjureMagicInteger() ; // better
 int a = ConjureMagicInteger(); // best

#2 To capture the result of an asynchronous operation

 auto asyncResult = std::async(&ConjureMagic);
 auto v = asyncResult.get();

The async() operation returns a std::future object that can be queried for the result once the async operation has finished. The issue with the code above is that I have no idea what variable v is. Of course, I can use contextual evidence or use the Visual Studio intellisence to figure out the type – but it's just more convenient to be more specific about the future type. For example, we can rewrite the same piece of code as:

 std::future<int> asyncResult2 = std::async(&ConjureMagic);
 int v = asyncResult2.get();

Final words on C++ 11 Auto

The main thing to remember while using auto is this : Use auto where ever you believe it enhances code readability and avoid it where ever it obscures the intent of the code. At the end of the day, you're writing the piece of code for the next person who picks up your module and not the compiler , right ? 🙂

  • Pingback: 3 – C++ 11 Auto: How to use and avoid abuse()

  • Pingback: C++ 11 Auto: How to use and avoid abuse – Daily Hackers News()

  • #2 To capture the result of an asynchronous operation
    auto v = asyncResult.get();
    int v = asyncResult.get();

    I still have no idea what variable v is even without auto, I know its type but that does not help. Name your variables properly and there is no issue.

    • Mikulas, good point – you can tell the snippets are not production code 🙂 Yes, naming the variable correctly will make the context more apparent.

      The issue i face on some extremely large VS2012 game projects is that the intellisense takes time to catch up once you open the solution and sometimes intellisense is not great on templatized types. If i just need to add a few lines after the async call to process the data returned, i need to wait or figure out the type by reading the code. It gets annoying for custom data types. One of my colleagues uses vim editor, so it’s even worse for him as there is no intellisense.

  • Rastervision

    Doesn’t this go against the advice of “don’t write code for yourself, write code for the people that have to read your code”? The person reading your Map iterator still has to figure out the type of the map value (and the map key).

    • +1 on “don’t write code for yourself, write code for the people that have to read your code” 🙂

      Using Auto in loops is somewhat of an acquired taste. I used to not like it before – but have grown fond of it since. Majority of Microsoft internal devs I’ve worked with as well as game studio devs seems to prefer this notation. In the end , you should do what your internal code review guidelines says and what the devs around you actually prefers.

  • Pingback: Reading List 02/15/2015 – thehumble.ninja()

  • Richard

    Pretty cool, made me feel rusty. Too bad, plenty of developers resiste changes, even after 5 years since the standard was published…

    • Yes Richard – I’ve noticed the inertia among developers as well. I believe it’s a good thing to use the new standard for new code. For old but stable code bases though, my philosophy remains ” don’t fix something that ain’t broken “:)

  • hcg2007

    I disagree with #1, for two reasons:

    1. If ‘a’ is just a temp variable with very minimal scope, as in your example, then auto is an ideal usage because you don’t really care what its type is.

    2. I feel that including the return type (or especially the parameter type) in the name of a function is not a good idea. It reduces readability by exposing unnecessary details. If you really care what the type is, you can look it up.

    • Good points.
      If ‘a’ is very short lived, like in a loop or something – using auto is probably fine. The problem happens when ‘a’ is intercepted and acted upon by other functions and you’re looking at it in notepad of VIM.
      I tend to agree with your second point and don’t use it personally – but I’ve seen many codebases where folks use it. So yes, return type should not be used to fill the gap of bad naming conventions in general.