Поиск…


замечания

Ключевое слово 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

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 имеет больше смысла.

Другим случаем, когда объекты-прокси могут вызывать проблемы, являются шаблоны выражений . В этом случае шаблоны иногда не рассчитаны на то, чтобы оставаться выше текущего полного выражения для эффективности, а использование прокси-объекта в следующем случае приводит к неопределенному поведению.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow