C++11: auto

C++ does not have any mechanism for querying the type of an expression nor it can initialize a variable without explicitly specifying its type. Specifically to address this limitation two new extensions have been added to C++11: decltype – to query the type of an expression auto – indicate that the compiler should deduce the type of the variable from its initializer expression. In this post I will only cover auto.

auto keyword was used in C as storage specifier and was supposed to indicate that qualified variable is local. With C++ this became optional.

For ex:

1
2
3
4
5
6
int main()
{
    // localvar is of type int
    auto int localvar = 9;
    return 0;
}

This code will compile fine in VS 2008 or C++2003, here storage qualifier auto is neglected as the variable localvar has local scope. With C++11 this has changed. auto has been redefined to indicate automatic type deduction by compiler, so this code fails to compile with error: auto cannot be combined with any other type specifier

auto 101


Lets look at some of the simpler auto deductions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// i is of type int
auto i = 9;
//
//
// d is of type double
auto d = 0.9;
//
//
// iptr1 is of type int*
auto iptr1 = &i;
//
//
// iptr2 is of type int*
 auto* iptr2 = &i;
//
//
// iptr3 is of type int*
 auto iptr3 = iptr2;
 //
 //
 // iref1 is of type int&
 auto& iref1 = i;
//
//
// iref2 is of type const int&
const auto& iref2 = iref1;
//
//
// i2 is of type int
auto i2 = iref2;
//
//
// c1 is char
auto c1 = 'a';
//
//
// s1 is const char*
auto s1 = "auto";

you can use auto to deduce types of multiple declarations, but the base type of these should be same:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// i1 and i2 are of type int
// while i3 is of type int&
//
auto i1 = 0, i2 = 1, &i3 = i1;
//
//
// v is of type int* while u is of type int
// u and v has same base type (int)
//
auto *v = &i, u = 6;
//
//
// all the variables must have same base-type
// here a is an int, b a float
// and c is double*
// this line of code fails to compile with error message:
// "in a declarator list auto must always deduce to same type"
//
auto a = 10, b = 3.f , * c = new double;
//

How about initializing array? Please see this link why type can’t be deduced in case of an array.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// cannot deduce an array of elements with auto
// fails to compile with error message:
// "the element type of an array cannot be a type that contains 'auto'"
auto arr1[] = {'a','b','c'};
//
//
// too many initializers auto arr2 = {'a','b','c'};
// cannot deduce type for auto* from char
// cannot convert from char to int*
auto* arr3 = {'a','b','c'};

auto can deduce the types from return types of functions, after all it is an expression:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
double SomeFunction()
{
    return 0.0;
}
//
//
int main()
{
    // retval is of type double
    auto retval = SomeFunction();
    //
    //
    return 0;
}

How does auto behaves with implicit conversion?

Consider a class AutoDemo, its instance can be readily converted to double, but during deduction as in first statement in main, autoVar1 is deduced to be of type AutoDemo, rather than double. But if you want it to be double then you will have to explicitly convert it to double as with second statement.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class AutoDemo
{
public:
    AutoDemo()
    {}
    operator double()
    {
        return 8.0;
    }
};

int main()
{
    // autoVar1 is of type AutoDemo
    auto autoVar1 = AutoDemo();
    //
    //
    // autoVar2 is of type double
    auto autoVar2 = static_cast<double>(autoVar1);
    //
    //
    return 0;
}

one more example of type deduction with operators: Here Vector class defines a operator* to multiply two vectors and returns double, again with auto compiler is smart enough to figure out that the expression will result in double

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Vector
{
public:
    double operator*(const Vector& other) const {
        return 0.0;
    }
};
//
//
int main()
{
    Vector v1;
    Vector v2;
    //
    // dotProd is of type double
    auto dotProd = v1 * v2;
    //
    //
    return 0;
}

So far so good how about references with classes in a hierarchy? I have two classes A & B, where B publicly derives from A and is polymorphic.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
//
class A
{
    int a;
public:
    A() : a(0)
    {}
    //
    A(const A& other) : a(other.a)
    {}
    //
    virtual ~A()
    {}
    //
    //
    virtual void Print()
    {
        std::cout << "Calling from A::Print" << std::endl;
    }
};
//
//
class B : public A
{
    double d;
public:
    B() : d(0.0)
    {}
    //
    B(const B& other) : d(other.d)
    {}
    //
    virtual ~B()
    {}
    //
    //
    virtual void Print()
    {
        std::cout << "Calling from B::Print" << std::endl;
    }
};
//
//
//
int main()
{
    A* bptr = new B;
    // a is of type A
    // no polymorphic behaviour is preserved
    // object-slicing, as it happens today
    //
    auto a = *bptr;
    //
    //
    a.Print();
    //
    //
    bptr->Print();
    //
    //
    return 0;
}

what would be the output of the two print statements? In case of pointer it would be polymorphic, and Print function on class B would be invoked, however in case of a, this is not polymorphic. Why? bptr has been defined as A, and since C++ is a statically typed language, bptr will be converted to A rather than B. This complies with existing behaviour, otherwise knows as object slicing. Had it been reference, it would have been polymorphic

1
2
auto& b = *bptr;
b.Print();

Using auto


Consider the simplest example, looping over a vector and printing it’s contents to console:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <vector>
//
//
 int main()
{
    std::vector<int> vec(10);
    // pre-auto this would have looked like:
    for( std::vector<int>::iterator iter = vec.begin(), endIter = vec.end();
        iter != endIter; ++iter )
    {
        std::cout << *iter << std::endl;
    }
    //
    //
    // with auto:
    for( auto iter = vec.begin(), end = vec.end();
        iter != end; ++iter )
    {
        std::cout << *iter << std::endl;
    }
    //
    //
    //
    return 0;
}

same example using functors, lambda and comparing all versions: (here for more on C++11 lambda functions)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <vector>
#include <algorithm>
//
//
struct PrintFunction
{
    void operator()(int val)
    {
        std::cout << val << std::endl;
    }
};
//
//
//
int main()
{
    std::vector<int> vec(10);
    //
    //
    // pre-auto this would have looked like:
    for( std::vector<int>::iterator iter = vec.begin(), endIter = vec.end();
        iter != endIter; ++iter )
    {
        std::cout << *iter << std::endl;
    }
    //
    //
    // with auto:
    for( auto iter = vec.begin(), end = vec.end();
        iter != end; ++iter )
    {
        std::cout << *iter << std::endl;
    }
    //
    //
    // with functor with hand-written for loop:
    PrintFunction printFunction;
    for( std::vector<int>::iterator iter = vec.begin(), endIter = vec.end();
        iter != endIter; ++iter )
    {
        printFunction(*iter);
    }
    //
    //
    // using functor with for_each algorithm
    std::for_each(vec.begin(), vec.end(), printFunction);
    //
    //
    // using lambda:
    // type of printFunctionLambda is --> void printFunctionLambda(int)
    auto printFunctionLambda = [](int val){std::cout << val << std::endl;};
    //
    //
    // with lambda with hand-written for loop:
    for( auto iter = vec.begin(), endIter = vec.end();
        iter != endIter; ++iter )
    {
        printFunctionLambda(*iter);
    }
    //
    // using functor with for_each algorithm:
    std::for_each(vec.begin(), vec.end(), printFunctionLambda);
    //
    //
    //
    return 0;
}

Thus auto is syntactic sugar, but as sugar it is sweet. It will help to write much more readable code. I hope this post will be good to get you started with auto. In future we will be using auto extensively. All the code can be tried out in VS 2010 SP1.