46

When I use auto to deduce a pointer type, I found a weird phenomenon. My code is like this:

#include <iostream>
using namespace std;
int main()
{
    int i = 100;
    auto p1 = &i;
    auto *p2 = &i;

    cout << *p1 << " " << *p2 << endl;

    return 0;
}

After compiling and executing, we can find that the result of *p1 and *p2 is the same, both 100. This means p1 and p2 are both a pointer object which points to an int object.

[user@host ~]$ ./test 
100 100

Is there any difference between these two statements which define p1 and p2?

  • 9
    Of course, what else could possibly happen? If p1 and p2 were not both pointers then you couldn't initialize them with `&i`. Maybe you should write (and think) in terms of `auto* p2` not `auto *p2` to make it clear that that `p2` is a variable of a pointer type, with the pointed-to type automatically deduced by the compiler. – Jonathan Wakely Nov 13 '15 at 08:46
  • 1
    `auto* p2 = init()` enforces raw pointer type. Might be useful if e.g. someday `init()` is refactored to return `shared_ptr` and instead of copying this value and keeping that `T` in memory, you get a compilation error and have a chance to decide whether to use `shared_ptr`, `weak_ptr`, or `T&`. – jingyu9575 Nov 13 '15 at 12:11
  • 1
    This is why `auto` is both magical and potentially confusing. – Lightness Races in Orbit Nov 14 '15 at 15:14

2 Answers2

84

The difference is that in the first case auto is deduced to int* while in the second case auto is deduced to int, which results in both p1 and p2 being of type int*. The type deduction mechanism for auto is equivalent to that of template arguments. The type deduction in your example is therefore similar to

template<typename T>
void foo(T p1);

template<typename T>
void bar(T* p2);

int main()
{
    int i;
    foo(&i);
    bar(&i);
}

where both functions are instantiated as type void(int*) but in the first case T is deduced to int* while in the second case T has type int.

nh_
  • 2,211
  • 14
  • 24
20

auto specifier used in declarations of variables, deduces its type with the same rules as used in template argument deduction.

Consider your first example (i.e., auto p1 = &i;). The type of auto specifier is deduced as follows:

  1. auto is replaced with an imaginary type template parameter (e.g., U p1 = &i;).
  2. &i type is int*, thus with no surprises and according to template deduction rules U is deduced to int*.

Now consider your second example (i.e., auto *p2 = &i).

  1. Again auto is replaced with an imaginary type template parameter (e.g., U* p1 = &i;).
  2. &i type is int*, thus according to template deduction rules U is deduced to int.

As such, in auto *p2 = &i; the placeholder type auto will correctly be deduced as int and not as int*, that would result in p2 being of type int**, as you might have expected.

101010
  • 41,839
  • 11
  • 94
  • 168
  • 2
    +1 for mentioning template argument deduction rule. However, as a note, `auto` type deduction has one small difference from template type deduction (see Meyers' Effective Modern C++), although I don't remember the details. – Jordan Melo Nov 13 '15 at 18:40
  • 3
    The difference is due to curly brace initializer lists: auto x = {1,2,3}. Cited from Scott Meyers book (Effective modern c++): "auto type deduction is usually the same as template type deduction, but auto type deduction assumes that a braced initializer represents a std::initializer_list, and template type deduction doesn’t.". – spraetor Nov 14 '15 at 12:07