Поиск…
замечания
Ключевое слово auto
- это имя-тип, представляющий автоматически выведенный тип.
Это уже зарезервированное ключевое слово в C ++ 98, унаследованное от C. В старых версиях C ++ его можно было использовать для явного указания, что переменная имеет автоматическую продолжительность хранения:
int main()
{
auto int i = 5; // removing auto has no effect
}
Этот старый смысл теперь удален.
Основной автоматический образец
Ключевое слово auto
предоставляет автоматический вывод типа переменной.
Это особенно удобно при работе с длинными именами типов:
std::map< std::string, std::shared_ptr< Widget > > table;
// C++98
std::map< std::string, std::shared_ptr< Widget > >::iterator i = table.find( "42" );
// C++11/14/17
auto j = table.find( "42" );
vector<int> v = {0, 1, 2, 3, 4, 5};
for(auto n: v)
std::cout << n << ' ';
с лямбдами :
auto f = [](){ std::cout << "lambda\n"; };
f();
чтобы избежать повторения типа:
auto w = std::make_shared< Widget >();
чтобы избежать неожиданных и ненужных копий:
auto myMap = std::map<int,float>();
myMap.emplace(1,3.14);
std::pair<int,float> const& firstPair2 = *myMap.begin(); // copy!
auto const& firstPair = *myMap.begin(); // no copy!
Причиной для копирования является то, что возвращаемый тип фактически является std::pair<const int,float>
!
авто и экспрессии
auto
также может вызвать проблемы, в которые входят шаблоны выражения:
auto mult(int c) {
return c * std::valarray<int>{1};
}
auto v = mult(3);
std::cout << v[0]; // some value that could be, but almost certainly is not, 3.
Причина в том, что operator*
на valarray
дает вам прокси-объект, который ссылается на valarray
как средство ленивой оценки. Используя auto
, вы создаете оборванную ссылку. Вместо mult
вернул std::valarray<int>
, тогда код определенно напечатает 3.
auto, const и ссылки
Ключевое слово auto
представляет собой тип значения, аналогичный int
или char
. Он может быть изменен с помощью ключевого слова const
и символа &
для представления типа const или ссылочного типа, соответственно. Эти модификаторы можно комбинировать.
В этом примере s
является типом значения (его тип будет выведен как std::string
), поэтому каждая итерация цикла for
копирует строку из вектора в s
.
std::vector<std::string> strings = { "stuff", "things", "misc" };
for(auto s : strings) {
std::cout << s << std::endl;
}
Если тело цикла изменяет s
(например, вызывая s.append(" and stuff")
), будет изменена только эта копия, а не исходный член strings
.
С другой стороны, если s
объявлено с auto&
он будет ссылочным типом (предполагается, что он является std::string&
), поэтому на каждой итерации цикла ему будет назначена ссылка на строку в векторе:
for(auto& s : strings) {
std::cout << s << std::endl;
}
В теле этого цикла изменения s
будут непосредственно влиять на элемент strings
который он ссылается.
Наконец, если s
объявлен const auto&
, это будет тип ссылки const, что означает, что на каждой итерации цикла ему будет назначена ссылка const на строку в векторе:
for(const auto& s : strings) {
std::cout << s << std::endl;
}
Внутри тела этого цикла s
не может быть изменен (т.е. на нем не могут быть вызваны неконстантные методы).
При использовании auto
с использованием циклов, основанных for
диапазонах, обычно рекомендуется использовать const auto&
если тело цикла не будет изменять структуру, зацикливаемую, поскольку это позволяет избежать ненужных копий.
Обратный тип возврата
auto
используется в синтаксисе для возвращаемого возвращаемого типа:
auto main() -> int {}
что эквивалентно
int main() {}
В основном полезно в сочетании с decltype
использовать параметры вместо std::declval<T>
:
template <typename T1, typename T2>
auto Add(const T1& lhs, const T2& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }
Общий лямбда (C ++ 14)
C ++ 14 позволяет использовать auto
в аргументе лямбда
auto print = [](const auto& arg) { std::cout << arg << std::endl; };
print(42);
print("hello world");
Эта лямбда в основном эквивалентна
struct lambda {
template <typename T>
auto operator ()(const T& arg) const {
std::cout << arg << std::endl;
}
};
а потом
lambda print;
print(42);
print("hello world");
авто и прокси-объекты
Иногда auto
может вести себя не так, как ожидал программист. Этот тип выводит выражение, даже если вывод типа не подходит.
Например, когда в коде используются прокси-объекты:
std::vector<bool> flags{true, true, false};
auto flag = flags[0];
flags.push_back(true);
Здесь flag
будет не bool
, а std::vector<bool>::reference
, так как для bool
специализации vector
шаблона operator []
возвращает прокси-объект с оператором operator bool
преобразования operator bool
.
Когда flags.push_back(true)
изменяет контейнер, эта псевдо-ссылка может оказаться свисающей, ссылаясь на элемент, который больше не существует.
Это также делает возможной следующую ситуацию:
void foo(bool b);
std::vector<bool> getFlags();
auto flag = getFlags()[5];
foo(flag);
vector
немедленно отбрасывается, поэтому flag
является псевдо-ссылкой на элемент, который был отброшен. Вызов foo
вызывает неопределенное поведение.
В таких случаях вы можете объявить переменную с auto
и инициализировать ее, выбрав тип, который вы хотите вывести:
auto flag = static_cast<bool>(getFlags()[5]);
но в этот момент просто замена auto
на bool
имеет больше смысла.
Другим случаем, когда объекты-прокси могут вызывать проблемы, являются шаблоны выражений . В этом случае шаблоны иногда не рассчитаны на то, чтобы оставаться выше текущего полного выражения для эффективности, а использование прокси-объекта в следующем случае приводит к неопределенному поведению.