C++ Insights - Type Deduction

Contents[Show]

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 with auto and decltype a new form 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;
}

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 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 as 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: Paul Baxter,  Meeting C++, Matt Braun, Avi Lachmish, Roman Postanciuc, Venkata Ramesh Gudpati, Tobias Zindl, Dilettant, Marko, Ramesh Jangama, G Prvulovic, and Reiner Eiteljörge.

Thanks in particular to:  TakeUpCode 450 60

 

Get your e-book at Leanpub:

The C++ Standard Library

 

Concurrency With Modern C++

 

Get Both as one Bundle

cover   ConcurrencyCoverFrame   bundle
With C++11, C++14, and C++17 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages.  

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.

I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more the 100 source files.

 

Get my books "The C++ Standard Library" (including C++17) and "Concurrency with Modern C++" in a bundle.

In sum, you get more than 600 pages full of modern C++ and more than 100 source files presenting concurrency in practice.

 

Get your interactive course

 

Modern C++ Concurrency in Practice

C++ Standard Library including C++14 & C++17

educative CLibrary

Based on my book "Concurrency with Modern C++" educative.io created an interactive course.

What's Inside?

  • 140 lessons
  • 110 code playgrounds => Runs in the browser
  • 78 code snippets
  • 55 illustrations

Based on my book "The C++ Standard Library" educative.io created an interactive course.

What's Inside?

  • 149 lessons
  • 111 code playgrounds => Runs in the browser
  • 164 code snippets
  • 25 illustrations
Tags: auto, decltype

Add comment


My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 262

All 2993015

Currently are 145 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments