const auto* versus const auto for Pointer Types

When working with references, C++ developers have been trained to use auto&, const auto&, or auto&& to avoid copies being made. However, when dealing with pointers, developers often do not use auto* but simply use auto instead. With pointers, there is no danger that you make accidental copies of the data, but there are other issues with omitting the *. Also, when working with pointers, there is a big difference between using const auto versus const auto*.

Let’s assume you have the following simple classes:

#include <memory>

class Data {};

class Foo
{
public:
	Foo() : m_data(std::make_unique<Data>()) { }

	Data* GetData() { return m_data.get(); }

private:
	std::unique_ptr<Data> m_data;
};

The GetData() method simply returns a pointer to a Data instance.

When using auto, a lot of developers simply write the following:

Foo foo;
auto d1 = foo.GetData();

The resulting type of d1 is Data*.

I actually recommend to write the following instead:

auto* d2 = foo.GetData();

d2 is also of type Data*, but the benefit is that you immediately see that you are dealing with a pointer. I know, in most IDE’s you can simply hover your mouse over the variable name and it will tell you the exact type. However, sometimes you are not working in an IDE. One such example is when doing code review in an external tool. Most of these tools do not show you that information when hovering over the name of a variable. This makes it a bit more difficult during code review to know that d2 is actually a pointer. When you write auto*, then it’s immediately obvious.

Now, let’s throw const into the mix. Again, most developers will not write the * with auto, so they write the following:

const auto d3 = foo.GetData();

However, this is most of the time not doing what you expect it to do!
Often, when you use const, you want to protect the thing to which the pointer is pointing to. A lot of developers assume that d3 is of type const Data*, but in fact, the type is Data* const, so it’s a const pointer to a non-const Data! Putting the const after the auto as follows doesn’t help, the type is still Data* const:

auto const d4 = foo.GetData();

When you use auto* in combination with const, then it’s behaving as you would expect. For example:

const auto* d5 = foo.GetData();

With this line, d5 is of type const Data* 🙂

If you really want a const pointer instead of const data, you put the const at the end:

auto* const d6 = foo.GetData();

d6 has type Data* const.

And finally, with this syntax you can make both the pointer and the data constant:

const auto* const d7 = foo.GetData();

d7 is of type const Data* const. You cannot achieve this if you omit the *.

All this is something to keep in mind. I’ve seen developers make mistakes against this several times.

Share

7 Comments so far »

  1. Brian said,

    Wrote on July 10, 2018 @ 9:55 pm

    I decided to stop using auto and use char const* in several places after thinking about this. Thank you.

  2. lvccgd said,

    Wrote on July 11, 2018 @ 10:45 am

    The main profit is expression becomes readable as for me. What do you think about the same but for refs:
    template
    struct Foo
    {
    T data;
    T GetData() const { return data; }

    Foo foo;
    const auto& d = foo.GetData();

  3. Marc Gregoire said,

    Wrote on July 23, 2018 @ 6:42 pm

    Your GetData() is returning a T by value, so you should just use auto or const auto.
    However, if your GetData() returns a reference (T&), then you should indeed use auto& or const auto&, otherwise a copy will be made, which is probably not what you want.

  4. Jampandu said,

    Wrote on October 5, 2018 @ 5:57 am

    Thank you for sharing article. I always confuse with const auto and const auto*.
    You mentioned in the article

    “A lot of developers assume that d3 is of type const Data*, but in fact, the type is Data* const,…”

    Any reason why cpp committee /compiler chosen this confusing syntax and why cant cpp compiler just deduce it as what most developers assuming as its stright forward to understand no need to put extra effort while writing the code.

  5. Marc Gregoire said,

    Wrote on October 6, 2018 @ 6:17 pm

    I have no idea why they chose this behavior.

  6. Jampandu said,

    Wrote on February 5, 2019 @ 11:34 am

    Seems this could be the reason. Let me know your opinion.
    I understood, CPP committee has chosen type deductions for auto based on template type deduction.
    And C+11 has a mapping between template type deduction and auto type deduction

    Ex:-

    Case 1:-

    const auto d3 = foo.GetData();
    compiler maps auto to a template function like

    template
    Foo(const T d3) { } This template function describes d3 is an const T.
    When we pass the pointer, It d3 becomes a const Pointer not an pointer to const type.

    Case 2:-,

    const auto *d3 = foo.GetData();
    compiler maps auto to a template function like

    template
    Foo(const T *d3) { } This template function describes d3 is an pointer to an const type of T.
    When we pass the pointer, It d3 becomes a pointer to const type .

  7. Marc Gregoire said,

    Wrote on February 10, 2019 @ 2:14 pm

    The C++ standard states the following in section [dcl.type.auto.deduct]:

    If the placeholder is the auto type-specifier, the deduced type T0 replacing T is determined using the rules for template argument deduction.

Comment RSS · TrackBack URI

Leave a Comment

Name: (Required)

E-mail: (Required)

Website:

Comment: