Rule A7-1-5 (required, implementation, automated)

The auto specifier shall not be used apart from following cases: (1) to declare that a variable has the same type as return type of a function call, (2) to declare that a variable has the same type as initializer of non-fundamental type, (3) to declare parameters of a generic lambda expression, (4) to declare a function template using trailing return type syntax.

Rationale

Using the auto specifier may lead to unexpected type deduction results, and therefore to developers confusion. In most cases using the auto specifier makes the code less readable. Note that it is allowed to use the auto specifier in following cases:

  1. When declaring a variable that is initialized with a function call or initializer of non-fundamental type. Using the auto specifier for implicit type deduction in such cases will ensure that no unexpected implicit conversions will occur. In such case, explicit type declaration would not aid readability of the code.
  2. When declaring a generic lambda expression with auto parameters
  3. When declaring a function template using trailing return type syntax

Example

// $Id: A7-1-5.cpp 289436 2017-10-04 10:45:23Z michal.szczepankiewicz $
#include <cstdint>
#include <vector>

class A
{
};
void F1() noexcept
{
auto x1 = 5;// Non-compliant - initializer is of fundamental type
auto x2 = 0.3F; // Non-compliant - initializer is of fundamental type
auto x3 = {8};
// Non-compliant - initializer is of fundamental type

std::vector<std::int32_t> v;
auto x4 = v.size(); // Compliant with case (1) - x4 is of size_t type that
// is returned from v.size() method

auto a = A{}; // Compliant with case (2)

auto lambda1 = []() -> std::uint16_t {
return 5U;
}; // Compliant with case (2) - lambda1 is of non-fundamental lambda
// expression type
auto x5 = lambda1(); // Compliant with case (1) - x5 is of
// std::uint16_t type
}
void F2() noexcept
{
auto lambda1 = [](auto x, auto y) -> decltype(x + y) {
return (x + y);
};
// Compliant with cases (2) and (3)
auto y1 = lambda1(5.0, 10); // Compliant with case (1)
}
template <typename T, typename U>
auto F3(T t, U u) noexcept -> decltype(t + u) // Compliant with case (4)
{
return (t + u);
}
template <typename T>
class B
{

public:
T Fn(T t);
};
template <typename T>
auto B<T>::Fn(T t) -> T // Compliant with case (4)
{
// ...
return t;
}

See also

HIC++ v4.0 [9]: 7.1.8 Use auto id = expr when declaring a variable to have the same type as its initializer function call. C++ Core Guidelines [11]: Use auto. Google C++ Style Guide [12]: Use auto to avoid type names that are noisy, obvious, or unimportant.