Solarian Programmer

My programming ramblings

C++14 auto tutorial

Posted on August 21, 2014 by Sol

The C++14 standard was recently approved. Without bringing big changes to the language, like the previous standard did, the C++14 standard aims to make the programmer’s job easier by improving the C++11 standard.

C++11 introduced the auto keyword (technically auto was present even in the C++03 standard, however C++11 changed the meaning of auto), auto was meant to make the code cleaner and less error prone, by letting you write for example:

1     auto i = 1;
2     auto c = return_a_complex_number();

instead of:

1     int i = 1;
2     std::complex<double> c = return_a_complex_number();

The type of a variable declared as auto is deduced at compile time, and not at runtime, so using auto does not incur any speed penalty. This means that using auto with an uninitialized variable will rise an error, because the compiler has no way to know the type of the variable:

1     auto i;		// this will rise a compilation error

An inconsistency of the C++11 standard was that you weren’t allowed to use auto for the type of a function. This has changed in the new standard, e.g. now you can write:

1     // Error in C++11, works in C++14
2     auto my_function() {
3         ...
4         return value;
5     }

as long as your functions returns a value, the compiler knows how to interpret the auto keyword.

At the time of writing this article, you can use C++14 with the latest version of Clang and GCC. For e.g., to compile a C++ code with GCC you could use:

1     g++-4.9.1 -Wall -std=c++14 -pedantic my_prog_name.cpp -o my_prog_name

To better exemplify the way auto can simplify your code, let’s implement a piece of code in C++98, C++11 and C++14. Say that you want, for example, to modify the values of a vector by applying two functions in the same instruction, something like:

1     multiply_by_two(add_one(my_vector));	

Obviously, you could write a single function, instead of two, to add one and multiply by two the elements of a vector, the point here is not to write the most performant code, but to illustrate the syntax simplifications allowed by auto.

The C++98 version of multiply_by_two and add_one can be written as:

 1     std::vector<int>& add_one(std::vector<int> &v) {
 2         for(std::vector<int>::iterator it = v.begin(); it != v.end(); it++) {
 3             *it += 1;
 4         }
 5         return v;
 6     }
 7     
 8     void multiply_by_two(std::vector<int> &v) {
 9         for(std::vector<int>::iterator it = v.begin(); it != v.end(); it++) {
10             *it *= 2;
11         }
12     }

The above code is quite verbose for what it does.

Let’s see the C++11 version:

 1     std::vector<int>& add_one(std::vector<int> &v) {
 2         for(auto& it : v) {
 3             it += 1;
 4         }
 5         return v;
 6     }
 7 
 8     void multiply_by_two(std::vector<int> &v) {
 9         for(auto& it : v) {
10             it *= 2;
11         }    
12     }

The C++11 version is clearly an improvement, we were able to shorten a bit the code using auto and a for range loop. But the code is still verbose.

In C++14 we can use auto for a function type. The above code can be further simplified to:

 1     auto& add_one(std::vector<int>& v) {
 2         for(auto& it : v) {
 3             it += 1;
 4         }
 5         return v;
 6     }
 7 
 8     void multiply_by_two(std::vector<int>& v) {
 9         for(auto& it : v) {
10             it *= 2;
11         }    
12     }

The C++14 code is shorter, easier to read and more general than the C++11 version. Here is complete code:

 1     // C++14 "auto" demo code	
 2     #include <iostream>
 3     #include <vector>
 4 
 5     auto& add_one(std::vector<int>& v) {
 6         for(auto& it : v) {
 7             it += 1;
 8         }
 9         return v;
10     }
11 
12     void multiply_by_two(std::vector<int>& v) {
13         for(auto& it : v) {
14             it *= 2;
15         }    
16     }
17 
18     void print_vec(const std::vector<int>& v) {
19         for(const auto& e: v) {
20             std::cout << e << std::endl;
21         }   
22         std::cout << std::endl;     
23     }
24 
25     int main() {
26         // Add one and multiply by two a vector of integers
27         std::vector<int> my_vector{-1, 2, 3, 5};
28         multiply_by_two(add_one(my_vector));    
29         print_vec(my_vector);
30 
31         return 0;
32     }    

If you are tempted to use auto for declaring the type of the vectors from the main function don’t, auto paired with an initializer list (the {} syntax) defines a std::initializer_list which is a different than std::vector.

G++ 4.9.1 also supports unconstrained generic functions, basically you can use auto in a function parameter list, so the above code can be further simplified as :

 1     auto& add_one(auto& v) {
 2         for(auto& it : v) {
 3             it += 1;
 4         }
 5         return v;
 6     }
 7 
 8     void multiply_by_two(auto& v) {
 9         for(auto& it : v) {
10             it *= 2;
11         }    
12     }

unfortunately, this didn’t entered in the final C++14 standard. It is expected to be added later to the standard as a technical report.

In the initial version of this article I’ve wrongly assumed that the use of auto in a function parameter list was approved in the C++14 standard. I’ve corrected this error in the current version of the article.

If you are interested to learn more about the new C++11 syntax I would recommend reading The C++ Programming Language by Bjarne Stroustrup.

or, Professional C++ by M. Gregoire, N. A. Solter, S. J. Kleper 2nd edition:


Show Comments