| .. title:: clang-tidy - modernize-use-auto |
| |
| modernize-use-auto |
| ================== |
| |
| This check is responsible for using the ``auto`` type specifier for variable |
| declarations to *improve code readability and maintainability*. For example: |
| |
| .. code-block:: c++ |
| |
| 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: |
| |
| .. code-block:: c++ |
| |
| 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. |
| |
| .. code-block:: c++ |
| |
| 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: |
| |
| .. code-block:: c++ |
| |
| // 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 as ``std::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. |
| |
| .. code-block:: c++ |
| |
| 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. |
| |
| .. code-block:: c++ |
| |
| 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. |
| |
| .. code-block:: c++ |
| |
| 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 |
| ------- |
| |
| .. option:: 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 the :option:`RemoveStars` option (see below) is set to `true`, then ``*s`` |
| in the type are also counted as a part of the type name. |
| |
| .. code-block:: c++ |
| |
| // 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 = ... |
| |
| .. option:: 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: |
| |
| .. code-block:: c++ |
| |
| 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; |