02 type deduction

C++ Insights – Type Deduction

Andreas Fertigs story with C++ Insights goes on. This weeks post is about type deduction with auto and decltype or as I often phrase it: “Use the smartness of the compiler.”

02 type deduction

Type Deduction

 

With C++11 we got auto and decltype and, therefore, two new forms of type deduction. We are used to type deduction from templates, however, these two new variants can be tricky sometimes.

auto

 

Consider this example:

int main()
{
  int* ip;
  const int* cip;
  const int* const cicp = ip;

  auto aip = ip;
  auto acip = cip;
  auto acicp = cicp;
}

 

We have three different pointers, all of type int. They are getting more and more constified. The question is what’s the type that is deduced by auto? All are pointers, that’s for sure. But what happens with the const? auto removes all top-level qualifier. Hence, even the const disappears. Does it? Here is the output C++ Insights gives:

 

int main()
{

int * ip;
const int * cip;
const int *const cicp = ip;
int * aip = ip;
const int * acip = cip;
const int * acicp = cicp;
}

 

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Generic Programming (Templates) with C++": October 2024
  • "Embedded Programming with Modern C++": October 2024
  • "Clean Code: Best Practices for Modern C++": March 2025
  • Do you want to stay informed: Subscribe.

     

    Yes, the top-level const is removed. A constant pointer does not matter, so this const is discarded, but the constness of the memory behind it sticks. Hence, this const is preserved. Which is why acip looks exactly like acicp. This makes sense right.

    decltype(auto)

    Now, from time to time we like to preserve all qualifiers. This is when decltype appears. In contrast to auto, decltype does preserve all top-level qualifiers. With  C++14 the combination of decltype and auto is possible and we can write decltype(auto) which makes things easier. Here is another example from C++ Insights which uses C++14:

     

    int main()
    {
      int* ip;
      const int* cip;
      const int* const cicp = ip;
    
     
    
      decltype(auto) aip = ip;
      decltype(auto) acip = cip;
      decltype(auto) acicp = cicp;
    }
    

     

    From this we get the following output:

     

    int main()
    {

    int * ip;
    const int * cip;
    const int *const cicp = ip;
    int * aip = ip;
    const int * acip = cip;
    const int *const acicp = cicp;
    }

     

    We can see, that acicp does carry the second const which is lost when we just use auto.

    decltype

    When do we need decltype, or more precisely when do we like to keep all qualifiers? One popular reason is templates. Imagine a class template with some function Get. With just using auto it as a return type we can never return a reference to something. In template code, we often don’t know the exact types, which makes it desirable to provide code that just works. decltype can help here. However, consider decltype a library writer feature. In most cases, we are fine with auto. It is just good to know the entire toolbox.

    auto& and auto* versus auto

    What we‘ve seen so far is, that we need to be explicit, if it comes to auto and references. We must always write auto& to get a reference. How is it with pointers? There auto gives us the correct type, so we can spare the star? This is in fact a question I frequently get from my students. The answer is: it depends. I recommend writing it just for consistency. However, there are scenarios where we indeed need auto* even that auto did deduce the correct type. Consider this example:

    struct Foo{};
    
    Foo* GetFoo()
    {
       static Foo foo;
    
       return &foo;
    }
    
    int main()
    {
      auto fp = GetFoo();
    }
    

     

    We have a function returning a Foo* and an auto variable auto f = GetFoo() which deduces the type. Of course, the correct type. What if we’d like to make f const? That we cannot alter the data of f? Sure, we write it like this const auto f = .... At least that’s what we would do if we write it without auto. Here are some possibilities we can try:

    struct Foo{};
    
    Foo* GetFoo()
    {
       static Foo foo;
    
       return &foo;
    }
    
    int main()
    {
       auto fp0 = GetFoo();
       const auto fp1 = GetFoo();
       auto const fp2 = GetFoo();
       //const auto const fp3 = GetFoo(); does not compile
       const auto* fp4 = GetFoo();
       auto* const fp5 = GetFoo();
       const auto* const fp6 = GetFoo();
    }
    

     

    First, fp1 produces a const pointer to mutable data. Not exactly what we intended. fp2 probably seems pointless. fp3 makes more sense, but this doesn’t compile. The control changes, if we start using the form auto*. Now, we can add the qualifiers like we can do it with a regular type. But see for yourself in C++ Insights what the result is:

     

    struct Foo{/* public: inline constexpr Foo() noexcept; */
    /* public: inline constexpr Foo(const Foo &); */
    /* public: inline constexpr Foo(Foo &&); */
    };

    Foo * GetFoo()
    {
       static Foo foo = Foo();
       return &foo;
    }

    int main()
    {
       Foo * fp0 = GetFoo();
       Foo *const fp1 = GetFoo();
       Foo *const fp2 = GetFoo();
       const Foo * fp4 = GetFoo();
       Foo *const fp5 = GetFoo();
       const Foo *const fp6 = GetFoo();
    }

     

    The simple advice is: always be explicit and use the form auto& as well as auto* even if auto is able to deduce a pointer type.

     

    Let’s say we are explicit and use auto&. Look at this example:

    struct Singleton{};
    
    auto Get()
    {
       static Singleton s{};
    
       return s;
    }
    
    int main()
    {
       auto& x = Get();
    }
    

     

    We have a classical singleton which the function Get should return. Of course, we like a reference to it otherwise we have multiple tons. Despite auto and & this code does not compile:

    error: non-const lvalue reference to type 'Singleton' cannot bind to a temporary of type 'Singleton'

    The reason is, the Get in fact return Singleton and not Singleton&. Why? Because we did not apply the & to the auto-return type of Get. A small change and the code compiles:

    struct Singleton{};
    
    auto& Get()
    {
       static Singleton s{};
    
       return s;
    }
    
    int main()
    {
       auto& x = Get();
    }
    

     

    I’d like to thank Rainer for the opportunity to share information about C++ Insights on his popular blog!

    Have fun with C++ Insights. You can support the project by becoming a Patreon or of course with code contributions.

    Stay tuned for more insights about C++ Insights to template instantiation …

     

    Andreas

     

     

     

     

     

    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, 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, Stephen Kelley, Kyle Dean, Tusar Palauri, 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, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery,and Matt Godbolt.

    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

    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 *