auto-matically inititialized

Contents[Show]

Probably the most frequently used feature of C++11 is auto. Thanks to auto the compiler determines the type of a variable from its initializer. But what is the point with safety critical software? 

 

The facts of auto

Automatic type deduction with auto is extremely convenient. First, you save a lot of unnecessary typing, in particular with challenging template expressions; second, the compiler is never - in opposite to the programmer - wrong. 

I compare in the next listing the explicit and the deduced types.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <vector>

int myAdd(int a,int b){ return a+b; }

int main(){

  // define an int-value
  int i= 5;                                  // explicit
  auto i1= 5;                                // auto
 
  // define a reference to an int
  int& b= i;                                 // explicit
  auto& b1= i;                               // auto
 
  // define a pointer to a function
  int (*add)(int,int)= myAdd;               // explicit
  auto add1= myAdd;                         // auto
  
  // iterate through a vector
  std::vector<int> vec;
  for (std::vector<int>::iterator it= vec.begin(); it != vec.end(); ++it){} 
  for (auto it1= vec.begin(); it1 != vec.end(); ++it1) {}

}

 

The compiler uses the rules for template argument deduction for getting the type of the variable. Therefore, the outer const or volatile qualifier and references are removed. The next example shows this behaviour for constant and references. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int main(){
  
  int i= 2011;
  const int i2= 2014;
  const int& i3= i2;
  
  auto a2= i2;     // int
  auto a3= i3;     // int
  
}

 

But how can I be sure that a2 or a3 are of type int although I used a variable of type const int or const int& to initialize them? Sometimes I deduce it wrong. The answer is simple. The compiler knows the truth. The only declared class template GetType helps me a lot. 

template <typename T>
class GetType; 

 

If I use the only declared class template, the compiler will immediately complain. The definition is missing. That the characteristic I need. The compiler tells me exactly the type of the class template which can not be instantiated. At first to the extended source code. I disabled in the following source code the try to instantiate the only declared class template.

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <get_type.hpp>

int main(){
  
  int i= 2011;
  const int i2= 2014;
  // GetType<decltype(i2)> myType;
  const int& i3= i2;
  // GetType<decltype(i3)> myType;
  
  auto a2= i2; 
  // GetType<decltype(a2)> myType;
  auto a3= i3;
  // GetType<decltype(a3)> myType;
  
}

 

The GetType call in the lines 7,9, 12, and 14 uses the specifier decltype, which give you the exact type of the declared variable. The rest is only hard work. I successively commented out each GetType expression. A deep look into the error messages of the g++ compilers is very interesting.

 

 autoGetType

 

The key expressions of the error message have a red line. Impressed? But once more. What is the point with safety critical software?

Initialize me!

auto determines its type from an initializer. That simply means. Without an initializer there is no type and therefore no variable. To say it positive. The compiler takes care that each type is initialized. That is a nice side effect of auto that is mentioned too rarely. 

It makes no difference, whether you forgot to initialize a variable or didn't make it because of a wrong understanding of the language. The result is simply the same: undefined behaviour. With auto you can overcome this nasty errors. Be honest. Do you know all rules for the initialization of a variable? If yes, congratulations. If not, read the article default initialization and all referenced article in this article. I have no clue why they used the following statement: "objects with automatic storage duration (and their subobjects) are initialized to indeterminate values". This formulation causes more harm than good. Local variables will not be default initialized.

I modified the second program of default initialization to make the undefined behaviour more obvious.

 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
// init.cpp

#include <iostream>
#include <string>
 
struct T1 {};
 
struct T2{
    int mem;     // Not ok: indeterminate value
 public:
    T2() {} 
};
 
int n;          //  ok: initialized to 0
 
int main(){
  
  std::cout << std::endl;
  
  int n;               // Not ok: indeterminate value
  std::string s;       // ok: Invocation of the default constructor; initialized to "" 
  T1 t1;               // ok: Invocation of the default constructor 
  T2 t2;               // ok: Invocation of the default constructor
    
  std::cout << "::n " << ::n << std::endl;
  std::cout << "n: " << n << std::endl;
  std::cout << "s: " << s << std::endl;
  std::cout << "T2().mem: " << T2().mem << std::endl;
  
  std::cout << std::endl;
                      
}

 

At first to the scope resolutions operator :: in line 25. :: addresses the global scope. In our case the variable n in line 14. Curiously enough, the automatic variable n in line 25 has the value 0. n has an undefined value and therefore the program has undefined behaviour. That will not hold for the variable mem of the class T2. mem returns an undefined value.

 init

Now, I rewrite the program with the help of auto.

 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
// initAuto.cpp

#include <iostream>
#include <string>
 
struct T1 {};
 
struct T2{
    int mem= 0;  // auto mem= 0 is an error
 public:
    T2() {}
};
 
auto n= 0;
 
int main(){
  
  std::cout << std::endl;
  
  using namespace std::string_literals;
  
  auto n= 0;
  auto s=""s;      
  auto t1= T1();               
  auto t2= T2();
    
  std::cout << "::n " << ::n << std::endl;
  std::cout << "n: " << n << std::endl;
  std::cout << "s: " << s << std::endl;
  std::cout << "T2().mem: " << T2().mem << std::endl;
  
  std::cout << std::endl;
                      
}

 

Two lines in the source code are especially interesting. First, the line 9. The current standard forbid it to initialize non-constant members of a class with auto. Therefore, I have to use the explicit type. This is from my perspective contra intuitive. Here is a discussion of the C++ standardization committee about this issue: article 3897.pdf. Second, the line 23. C++14 gets C++ string literals. You build them by using a C string literal ("") and add the suffix s (""s). For convenience I imported the in line 20: using namespace std::string_literals

The output of the program is not so thrilling. Only for completeness. T2().mem has the value 0. 

 initAuto

Refactorization

Just at the moment I want to conclude the post a new use case of auto come in my mind. auto very well supports the refactorization of your code. First, it's very easy to restructure your code if there is no type information. Second, the compiler takes automatically care of the right types. What does that mean? I give the answer in the form of a code snippet. At first, the code without auto.

int a= 5;
int b= 10;
int sum=  a * b * 3;
int res= sum + 10; 

 

When I replace the variable b of type in by a double 10.5, I have to adjust all dependant types. That is laborious and dangerous. I have to use the right types and to take care of narrowing and other intelligent phenomenons in C++.

 

int a2= 5;
double b2= 10.5;
double sum2= a2 * b2 * 3;
double res2= sum2 * 10.5;

 

This danger is not present in case of auto. Everything happens auto-matically.

 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
// refactAuto.cpp

#include <typeinfo>
#include <iostream>

int main(){
  
  std::cout << std::endl;

  auto a= 5;
  auto b= 10;
  auto sum=  a * b * 3;
  auto res= sum + 10; 
  std::cout << "typeid(res).name(): " << typeid(res).name() << std::endl;
  
  auto a2= 5;
  auto b2= 10.5;
  auto sum2= a2 * b2 * 3;
  auto res2= sum2 * 10;  
  std::cout << "typeid(res2).name(): " << typeid(res2).name() << std::endl;
  
  auto a3= 5;
  auto b3= 10;
  auto sum3= a3 * b3 * 3.1f;
  auto res3= sum3 * 10;  
  std::cout << "typeid(res3).name(): " << typeid(res3).name() << std::endl;
  
  std::cout << std::endl;
   
}

 

The small variations of the code snippet always determines the right type of res, res2, or res3. That's the job of the compiler. The variable b2 in line 17 is of type double and therefore also res2; the variable sum3 in line 24 becomes due to the multiplication with the float literal 3.1f a float type and therefore also the finale result res3. To get the type from the compiler I use the typeid operator that is defined in the header typeinfo.

Here you get the results black on yellow.

 

 autoRefact

Impressed? Me too.

What's next?

The initialization with curly braces {} has a lot in common with auto. It is used similarly often, helps to read the code, and makes your code safer. How? You will see in the next post.

 

 

 

 

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Get your e-book. Support my blog.

 

 

 

Comments   

0 #1 Shafik Yaghmour 2016-10-13 19:23
Minor correction "glue" -> "clue".

As far as I can tell "Local variables will not be defaultl initialized" should be "Local variables will be default initialized" in the case of objects with automatic storage, non-class non-array types "nothing is done" or they have "indeterminate value".

As to why indeterminate values, the basic explanation I see if the base C++ mantra; "You don't pay for what you don't use".

Note it wasn't until C++14 that the C++ standard made it clear what uses of indeterminate values were undefined behavior.
Quote
0 #2 Rainer Grimm 2016-10-13 20:42
Quoting Shafik Yaghmour:
Minor correction "glue" -> "clue".

Thanks, I fix the typo.
Quote
0 #3 new car reviews 2016-10-15 11:52
It's hard to find educated people on this topic, but you seem like you know what you're talking about!
Thanks
Quote

Add comment


My Newest E-Books

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 70

All 388093

Currently are 176 guests and no members online