autoGetType

auto-matically inititialized

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 of safety-critical software? 

 

The facts about auto

Automatic type deduction with auto is exceptionally convenient. First, you save a lot of unnecessary typing, particularly with challenging template expressions; second, the compiler is never wrong – in opposition to the programmer. 

I compare, in the following 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) {}

}

 

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

Be part of my mentoring programs:

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (starts March 2024)
  • Do you want to stay informed: Subscribe.

     

    The compiler uses the rules for template argument deduction to get the variable type. Therefore, the outer const or volatile qualifier and references are removed. The following example shows this behavior 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 is 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; 
    

     

    The compiler will immediately complain if I use only the declared class template. The definition is missing. That is the characteristic I need. The compiler tells me exactly the type of class template, which can not be instantiated. At first, to the extended source code. I disabled 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 lines 7,9, 12, and 14 uses the specifier decltype, which gives you the exact type of the declared variable. The rest is only hard work. I successively commented on each GetType expression. A deep look into the error messages of the g++ compilers is very interesting.

     

     autoGetType

     

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

    Initialize me!

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

    Whether you forgot to initialize a variable or didn’t make it because of a wrong understanding of the language makes no difference. The result is simply the same: undefined behavior. With auto you can overcome these nasty errors. Be honest. Do you know all rules for the initialization of a variable? If yes, congratulations. If not, read the article’s default initialization and all referenced articles 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 behavior 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 behavior. 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, line 9. The current standard forbids 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, line 23. C++14 gets C++ string literals. You build them by using a C string literal (“”) and adding 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 now, I want to conclude the post a new use case of auto comes to 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 of information. Second, the compiler automatically takes care of the right types. What does that mean? I answer in the form of a code snippet. At first, the code was 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 with a double 10.5, I have to adjust all dependent types. That is laborious and dangerous. I have to use the right types and 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 the 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 slight variations of the code snippet always determine 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 the final result res3. To get the type from the compiler, I use the typeid operator defined in the header typeinfo.

    Here you get the results in black on yellow.

     

     autoRefact

    Impressed? Me too.

    What’s next?

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

     

     

     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, moon, and Philipp Lenk.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Seminars

    I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

    Standard Seminars (English/German)

    Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

    • C++ – The Core Language
    • C++ – The Standard Library
    • C++ – Compact
    • C++11 and C++14
    • Concurrency with Modern C++
    • Design Pattern and Architectural Pattern with C++
    • Embedded Programming with Modern C++
    • Generic Programming (Templates) with C++
    • Clean Code with Modern C++
    • C++20

    Online Seminars (German)

    Contact Me

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *