'How does const auto and auto const apply to pointers?

I was try out some code and am wondering how the const qualifier in C++ applies to pointer types when using auto.

int main()
{
  int foo = 1;
  int bar = 2;

  //Expected: const int * ptr_to_const_int = &foo;
  const auto ptr_to_const_int = &foo;

  //Expected: int * const const_ptr_to_int = &foo;
  auto const const_ptr_to_int = &foo;


  *ptr_to_const_int = 3; //Thought this would error
  //ptr_to_const_int = &bar; This does error.
  *const_ptr_to_int = 3;

  return 0;
}

I realize there is a similar question asking if they are the same, I am asking more specifically what is the rule here that is getting applied to the deduction of the end pointer type.



Solution 1:[1]

In this example, const is being applied to whatever auto deduces, which means both uses result in an object of type int * const, because auto on its own is deducing int *. The ordering that you're imagining takes place based on whether you write it auto const or const auto doesn't happen, in the same way that int const and const int are the same.

An easier way to think about this might be to try the following:

template<typename T>
using pointer = T*;

pointer<int> ptr_to_int = new int;
const pointer<int> const_ptr_to_int = new int;
pointer<const int> ptr_to_const_int = new int;
const pointer<const int> const_ptr_to_const_int = new int;

pointer<int> const const_ptr_to_int2 = new int;
pointer<int const> ptr_to_const_int2 = new int;
pointer<const int> const const_ptr_to_const_int2 = new int;

pointer<int const> const const_ptr_to_const_int3 = new int;

Any variables in this example, whose names only differ due to appended numbers, are equivalent types, as far as C++ is concerned. Note how changing where the const appears doesn't affect the deduced type. This is because the "read-right-to-left" rule for working out how types are declared is based on how raw types are written: once you use constructions like this (or, as you observed, auto) the rules become a lot simpler.

My instinct is that, since your problem sort of implies that you need this kind of fine-grained control over the type system anyways, you should use a using or typedef on pointer<T> like I've showcased here, and use that to declare your types, since it's a lot easier to know, at a glance, what const pointer<int> is than it is to see what int *const is. Especially since it prevents silly mistakes like this:

int * a_ptr, b_ptr, c_ptr; //Oops! We meant b_ptr and c_ptr to also be pointers, but
//they ended up being regular ints!

pointer<int> a_ptr, b_ptr, c_ptr; //All of these are pointers, as we expected them to be

auto technically solves this problem too, but as you're showing in your example, it's still ambiguous (to you) whether the const is being applied to the pointer itself, or the object it's pointing to, whereas in this case, there's no more ambiguity.

Solution 2:[2]

Place matters when there is * !!.

  1. Placing const before * makes value const.
  2. Placing const after * makes addr const.
  3. Placing const before/after * makes both value and addr const.

I have given a few possible combination of auto, const and * usage for better understanding.

    int main() {
        auto i = 2;               // int
        const auto j = 10;        // const int  
        
        // j = 20;                // Error: value is const

        const auto a = &i;        // int * const
        // a = &j;                // Error: addr is const
        *a = 4;                   // OK (value is NOT const)

        const auto* b = &i;       // const int *
        // *b = 4;                // Error: value is const
        b = &j;                   // OK (addr is NOT const)

        auto* const c = &i;       // const int *
        *c = 4;                   // OK (value is NOT const)
        // c = &j;                // Error: addr is const

        const auto* const d = &i; // const int * const
        // *d = 4;                // Error: Both value & addr are const
        // d = &j;                // Error: Both value & addr are const
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Xirema
Solution 2