Поиск…
замечания
Ключевое слово 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 имеет больше смысла.
Другим случаем, когда объекты-прокси могут вызывать проблемы, являются шаблоны выражений . В этом случае шаблоны иногда не рассчитаны на то, чтобы оставаться выше текущего полного выражения для эффективности, а использование прокси-объекта в следующем случае приводит к неопределенному поведению.