Search code examples
c++templatesautodecltypetype-deduction

What are the type deduction rules for auto*?


What are the type deduction rules for auto*?

Consider the following:

int x = 64;
int* px = &x;

auto* v1 = &x;    // auto => ???    ok v1 is int* ...
auto* v2 = px;    // auto => ???    is v2 int*  ?
auto* v3 = &px;   // auto => ???    is v3 int** ?

Just to clarify my question if we split the type deduction into two steps:

  1. Deducing the type of "auto" itself without (*) ... then
  2. Deducing the type of the object (v1, v2 and v3) after adding the (*)

So my two questions are:

  1. What will auto be deduced to without the (*) ?
  2. Will v2 be pointer to int (int*) and v3 pointer to pointer (int**) ?

Solution

  • If you know template type deduction you will know almost all there is to auto type deduction. Because auto type deduction works like template type deduction.

    When a variable is declared using auto, then auto acts as T in a template, and the type specifier acts as the parameter type:

    const auto i = 20;
    

    Would translate to:

    template<typename T>
    void func(const T param) { ... }
    //        ^^^^^^^
    

    And with reference:

    const auto& j = i;
    

    Translates to:

    template<typename T>
    void func(const T& param) 
    //        ^^^^^^^^
    

    With pointers, it's the same:

    auto* v1 = &x;
    

    Becomes

    template<typename T>
    void func(T* param)
    

    Since x is an int, then auto* == int*.
    And auto* v2 = px; is also int*

    Now, the third one you have:

    auto* v3 = &px;
    

    Becomes int** since you're taking the address of the pointer.

    template<typename T>
    void func(T** param)
    //        ^^^
    

    A handy way to see the type of auto is to use what others have mentioned, the typeid() function.
    But I like to use <boost/type_index.hpp> to show the type correctly:

    #include <iostream>
    #include <boost/type_index.hpp>
    using namespace std;
    using namespace boost::typeindex;
    
    int main()
    {
        int x = 64;
        int* px = &x;
    
        auto* v1 = &x;
        auto* v2 = px;
        auto* v3 = &px;
        cout << type_id_with_cvr<decltype(v1)>().pretty_name() << '\n';
        cout << type_id_with_cvr<decltype(v2)>().pretty_name() << '\n';
        cout << type_id_with_cvr<decltype(v3)>().pretty_name() << '\n';
    }
    

    Which outputs:

    int*
    int*
    int**
    

    There is one important difference between auto type deduction and template type deduction, namely std::initializer_list<>

    Consider these examples:

    auto i = 1;   // int
    auto j(1);    // int
    auto k = { 1 }// std::initializer_list<int> !
    auto l { 1 }  // std::initializer_list<int> !
    

    As you see, using brace initializer with auto can be trouble.
    You can however manually write the type before the braces to ensure that the type is correct but I don't see the point in that:

    auto i = int{ 1 }; // type is int
    

    There are new auto rules that have been implemented already in Clang 3.8 that makes it possible to use direct-list-initialization with auto (upcoming standard)