auto in modern c++

@jupp0r

static type checking

 

checks are performed by the compiler at compile time

 

correct types are guaranteed for all program inputs

dynamic type checking

 

checks are performed at runtime

type inference

instead of variable types having to be explicitly declared, the compiler deduces types by usage

// template type deduction - you already know this
template <typename T>
T add(T a, T b) {
  return a + b;
}

// auto type deduction - this is new in C++11
auto a = 12;
auto b = 3;
auto c = a + b; // 15

is this static or dynamic typing?

auto a = 12;
auto b = 3;
auto c = a + b;

what's the type of a, b, c?

auto a = 12;
auto b = 3;
auto c = a + b;

AAA

Almost

Always

Auto

AAA

Recommended by

  • Herb Sutter
  • Scott Meyers
  • Andrei Alexandrescu

readability

std::vector<int> numbers = {5, 12, 45, 13};
int sum = 0;

for(std::vector<int>::const_iterator i =
      numbers.cbegin();
    i != numbers.cend();
    i++) {
  sum = sum + *i;
}

readability

std::vector<int> numbers = {5, 12, 45, 13};
int sum = 0;

for(const auto i : numbers) {
  sum = sum + i;
}

maintainability

We'd like to change the container type of numbers

for(std::vector<int>::const_iterator i =
      numbers.cbegin();
    i != numbers.cend();
    i++) {
  sum = sum + *i;
}

maintainability

We'd like to change the container type of numbers

for(const auto i : numbers) {
  sum = sum + i;
}

No need to change anything.

correctness

int x;       // uninitialized variable,
             // reading its value is
             // undefined behavior

auto y;      // doesn't compile

auto z = 0;  // variables declared as auto
             // are guaranteed to be initialized
             // at compile time

performance

int sumWithAuto(int a, int b) {
	auto sum =
          [](int x, int y) {
    	    return x + y;
	  };
  return sum(a,b);
}
sumWithAuto(int, int):
        lea     eax, [rdi + rsi]
        ret

recent clang and gcc compile this to

performance

int sumWithoutAuto(int a, int b) {
  std::function<int(int,int)> sum =
    [](int x, int y) {
      return x + y;
    };
  return sum(a,b);
}

gcc 6.1 compiles this to ...

std::_Function_handler<int (int, int), sumWithoutAuto(int, int)::
           {lambda(int, int)#1}>::_M_invoke(std::_Any_data const&, int&&, std::_Any_data const&):
        mov     eax, DWORD PTR [rsi]
        add     eax, DWORD PTR [rdx]
        ret
std::_Function_base::_Base_manager<sumWithoutAuto(int, int)::{lambda(int, int)#1}>::
              _M_manager(std::_Any_data&, std::_Function_base::_Base_manager<
              sumWithoutAuto(int, int)::{lambda(int, int)#1}> const&, std::_Manager_operation):
        test    edx, edx
        je      .L4
        cmp     edx, 1
        jne     .L3
        mov     QWORD PTR [rdi], rsi
.L3:
        xor     eax, eax
        ret
.L4:
        mov     QWORD PTR [rdi], OFFSET FLAT:typeinfo for sumWithoutAuto(int, int)::{lambda(int, int)#1}
        xor     eax, eax
        ret

sumWithoutAuto(int, int):
        sub     rsp, 40
        lea     ecx, [rdi+rsi]
        mov     edx, 3
        mov     rsi, rsp
        mov     rdi, rsp
        mov     QWORD PTR [rsp+24], OFFSET FLAT:
                          std::_Function_handler<int (int, int), sumWithoutAuto(int, int)::
                          {lambda(int, int)#1}>::_M_invoke(
                          std::_Any_data const&, int&&, std::_Any_data const&)
        mov     QWORD PTR [rsp+16], OFFSET FLAT:std::_Function_base::_Base_manager<
                          sumWithoutAuto(int, int)::{lambda(int, int)#1}>::_M_manager(std::_Any_data&,
                          std::_Function_base::_Base_manager<sumWithoutAuto(int, int)::
                          {lambda(int, int)#1}> const&, std::_Manager_operation)
        call    std::_Function_base::_Base_manager<sumWithoutAuto(int, int)::
                          {lambda(int, int)#1}>::_M_manager(std::_Any_data&,
                          std::_Function_base::_Base_manager<sumWithoutAuto(int, int)::
                          {lambda(int, int)#1}> const&, std::_Manager_operation)
        add     rsp, 40
        mov     eax, ecx
        ret

But ...

Isn't this just being lazy to declare your types?

Isn't this just you being lazy to declare your types?

No, it's all about

  • readability
  • maintainability
  • correctness
  • performance

I'm scared because I dont see what types my variables have

I'm scared because I dont see what types my variables have

You already don't know the exact types that are used in your code, and that's good!

  • what type does std::vector<T>::size() return? You don't care as long as it compares to int.
  • what type does std::bind return?
  • does std::find<std::vector<T>>(...) return the same type as std::vector<T>::end() ? You probably don't care as long as you can compare the two.

You really want to know the types?

IDE is your friend

What about the almost in AAA?

// error, not moveable
auto lock = std::lock_guard<std::mutex>{ m }; 

// error, not moveable
auto ai   = std::atomic<int>{};

Thanks!

Questions?