modernize-use-auto¶
This check is responsible for using the auto
type specifier for variable
declarations to improve code readability and maintainability. For example:
std::vector<int>::iterator I = my_container.begin();
// transforms to:
auto I = my_container.begin();
The auto
type specifier will only be introduced in situations where the
variable type matches the type of the initializer expression. In other words
auto
should deduce the same type that was originally spelled in the source.
However, not every situation should be transformed:
int val = 42;
InfoStruct &I = SomeObject.getInfo();
// Should not become:
auto val = 42;
auto &I = SomeObject.getInfo();
In this example using auto
for builtins doesn’t improve readability. In
other situations it makes the code less self-documenting impairing readability
and maintainability. As a result, auto
is used only introduced in specific
situations described below.
Iterators¶
Iterator type specifiers tend to be long and used frequently, especially in loop constructs. Since the functions generating iterators have a common format, the type specifier can be replaced without obscuring the meaning of code while improving readability and maintainability.
for (std::vector<int>::iterator I = my_container.begin(),
E = my_container.end();
I != E; ++I) {
}
// becomes
for (auto I = my_container.begin(), E = my_container.end(); I != E; ++I) {
}
The check will only replace iterator type-specifiers when all of the following conditions are satisfied:
The iterator is for one of the standard containers in
std
namespace:array
deque
forward_list
list
vector
map
multimap
set
multiset
unordered_map
unordered_multimap
unordered_set
unordered_multiset
queue
priority_queue
stack
The iterator is one of the possible iterator types for standard containers:
iterator
reverse_iterator
const_iterator
const_reverse_iterator
In addition to using iterator types directly, typedefs or other ways of referring to those types are also allowed. However, implementation-specific types for which a type like
std::vector<int>::iterator
is itself a typedef will not be transformed. Consider the following examples:
// The following direct uses of iterator types will be transformed.
std::vector<int>::iterator I = MyVec.begin();
{
using namespace std;
list<int>::iterator I = MyList.begin();
}
// The type specifier for J would transform to auto since it's a typedef
// to a standard iterator type.
typedef std::map<int, std::string>::const_iterator map_iterator;
map_iterator J = MyMap.begin();
// The following implementation-specific iterator type for which
// std::vector<int>::iterator could be a typedef would not be transformed.
__gnu_cxx::__normal_iterator<int*, std::vector> K = MyVec.begin();
The initializer for the variable being declared is not a braced initializer list. Otherwise, use of
auto
would cause the type of the variable to be deduced asstd::initializer_list
.
New expressions¶
Frequently, when a pointer is declared and initialized with new
, the
pointee type is written twice: in the declaration type and in the
new
expression. In this case, the declaration type can be replaced with
auto
improving readability and maintainability.
TypeName *my_pointer = new TypeName(my_param);
// becomes
auto *my_pointer = new TypeName(my_param);
The check will also replace the declaration type in multiple declarations, if the following conditions are satisfied:
All declared variables have the same type (i.e. all of them are pointers to the same type).
All declared variables are initialized with a
new
expression.The types of all the new expressions are the same than the pointee of the declaration type.
TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
// becomes
auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
Cast expressions¶
Frequently, when a variable is declared and initialized with a cast, the
variable type is written twice: in the declaration type and in the
cast expression. In this case, the declaration type can be replaced with
auto
improving readability and maintainability.
TypeName *my_pointer = static_cast<TypeName>(my_param);
// becomes
auto *my_pointer = static_cast<TypeName>(my_param);
The check handles static_cast
, dynamic_cast
, const_cast
,
reinterpret_cast
, functional casts, C-style casts and function templates
that behave as casts, such as llvm::dyn_cast
, boost::lexical_cast
and
gsl::narrow_cast
. Calls to function templates are considered to behave as
casts if the first template argument is explicit and is a type, and the function
returns that type, or a pointer or reference to it.
Known Limitations¶
If the initializer is an explicit conversion constructor, the check will not replace the type specifier even though it would be safe to do so.
User-defined iterators are not handled at this time.
Options¶
- MinTypeNameLength¶
If the option is set to non-zero (default 5), the check will ignore type names having a length less than the option value. The option affects expressions only, not iterators. Spaces between multi-lexeme type names (
long int
) are considered as one. If theRemoveStars
option (see below) is set to true, then*s
in the type are also counted as a part of the type name.
// MinTypeNameLength = 0, RemoveStars=0
int a = static_cast<int>(foo()); // ---> auto a = ...
// length(bool *) = 4
bool *b = new bool; // ---> auto *b = ...
unsigned c = static_cast<unsigned>(foo()); // ---> auto c = ...
// MinTypeNameLength = 5, RemoveStars=0
int a = static_cast<int>(foo()); // ---> int a = ...
bool b = static_cast<bool>(foo()); // ---> bool b = ...
bool *pb = static_cast<bool*>(foo()); // ---> bool *pb = ...
unsigned c = static_cast<unsigned>(foo()); // ---> auto c = ...
// length(long <on-or-more-spaces> int) = 8
long int d = static_cast<long int>(foo()); // ---> auto d = ...
// MinTypeNameLength = 5, RemoveStars=1
int a = static_cast<int>(foo()); // ---> int a = ...
// length(int * * ) = 5
int **pa = static_cast<int**>(foo()); // ---> auto pa = ...
bool b = static_cast<bool>(foo()); // ---> bool b = ...
bool *pb = static_cast<bool*>(foo()); // ---> auto pb = ...
unsigned c = static_cast<unsigned>(foo()); // ---> auto c = ...
long int d = static_cast<long int>(foo()); // ---> auto d = ...
- RemoveStars¶
If the option is set to true (default is false), the check will remove stars from the non-typedef pointer types when replacing type names with
auto
. Otherwise, the check will leave stars. For example:
TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
// RemoveStars = 0
auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
// RemoveStars = 1
auto my_first_pointer = new TypeName, my_second_pointer = new TypeName;