Auto — плюсы и минусы

от автора

Вступление

Доброго времени суток, друзья! Сегодня речь пойдёт об операторе auto в C++.
Небольшая предыстория: данный оператор пришёл к нам в C++11 и с тех пор глубоко вошёл в жизнь многих программистов и различных проектов. Сегодня я бы хотел рассказать о некоторых плюсах и минусах с моей точки зрения.

А с чем его едят?

Итак, где же его лучше всего, как мне кажется, применять?

1. Lambda

Вслед auto с 11 стандартом языка к нам пришли lambda functions. Рассмотрим на примере:

    // C++98     struct      {         bool operator()(int a, int b) const         {                return a < b;         }        } customLess;     std::sort(s.begin(), s.end(), customLess);

Старый добрый std::sort с перегрузкой на свой алгоритм сортировки. В C++98 для такого кода требовалось создавать оболочку через структуру. С приходом лямбд ситуация сильно изменилась:

    // C++     std::sort(s.begin(), s.end(),      // Lambda begin     [](int a, int b)      {         return a > b;        }     // End     );

2. STD Типы

Все мы когда-нибудь использовали std и используем сейчас. Но std типы имеют одну большую проблему… Они слишком длинные.

    int n1 = 3;      std::vector<int> v{ 0, 1, 2, 3, 4 };         // Случай 1     std::vector<int>::iterator OldStyle = std::find(v.begin(), v.end(), n1);         // Случай 2     auto NewStyle = std::find(v.begin(), v.end(), n1);

Случай первый: std::find возвращает тип, равный std::vector<int>::iterator, Но мы это и сами прекрасно знаем. Во многих проектах, к примеру в том же игровом движке X-Ray, для таких итераций делались тайпдефы, аля:

typedef std::vector<int> vec_int; typedef vec_int::iterator vec_int_it;

Но я не фанат такого кода, поэтому мне проще воспользоваться auto, как показано во втором случае.

3. Range-base-for loops

Долгожданное нововведение в C++ было R-B-For циклы. Ну, как нововведение, в C# и C++/CLI они уже были, а нативный всё так же отставал…
Когда нам нужно перебрать, допустим, элементы std::map, приходится обращаться к std::pait. Но, не стоит забыть про auto! Рассмотрим ситуацию, обратившись к исходному коду xray-oxygen:

        for (auto &entities_it : entities)         {             if (entities_it.second->ID_Parent != 0xffff) continue;              found = true;             Perform_destroy(entities_it.second);             break;         }

Хороший вид цикла. Без auto было бы так:

        for (std::pair<const u16, CSE_Abstract*> &entities_it : entities)         {             if (entities_it.second->ID_Parent != 0xffff) continue;              found = true;             Perform_destroy(entities_it.second);             break;         } 

Как вы поняли, std::pair<const u16, CSE_Abstract*> будет отсылкой ко второму подзаголовку, ну да ладно, такие типы есть не только в STD Namespace, но и в самом X-Ray. Однако, в таких итераторах кроется ещё одна проблема, но об этом позже…

А чем же он плох?

1. Главная проблема — неясность типов…

Допустим, ситуация, имеем мы в коде:

auto WhyIsWhy = TestPointer->GetCrows();

Человеку, не писавшему этот код, нужно быстро глянуть содержимое функции, запускать IDE слишком долго, воспользуется Notepad++. Он натыкается на эту строку, и получает Logic Error начинается поиск объявления GetCrows функции.

2. Ссылка на объект массива или давайте поговорим ещё о Range-Base-For!

LimbUpdate(CIKLimb &refLimb); ... {        // Код из XRay Oxygen, система инверсной кинематики, не обращайте внимания,         // беру из того, что под рукой     for (auto it : _bone_chains)         LimbUpdate(it); }

Вроде бы всё хорошо, но! Мы работаем не с элементом массива, а с копией объекта элемента. Из-за auto мы невидим, что it у нас не поинтер, а объект. Поэтому в местах, где у нас одномерный массив, советую писать типы полностью:

    for (CIKLimb &it : _bone_chains)         LimbUpdate(it);

3. А у вас x64!

Те, кто работал в с написанием кода под AMD64, знают, что компилятор больше любит double, а не float. И наш друг auto кастуется во время компиляции в предпочитаемый программой тип. Рассмотрим на примере.

auto isFloat = 1.0; // 1.0f(x32) 1.0d(x64)

Конечно, такие вещи не критичны, но компилятор может начать сыпать кучу предупреждений по усечению типов, что не очень приятно, да и к тому же, постоянная запись в лог замедляет сборку. Но, есть ситуации, когда такой кастинг играет роль. В случаях с натуральным числом ‘e’ нужно быть аккуратным. К примеру:

auto eps = 1e-8; 

В x32 это значение равно: 9.999999939e-09
В x64 это значение равно: 1.0000000000000021e-08

Заключение

Конечно же, решать вам, где и как использовать auto, но мне больше нравится полноценная концепция статической типизации там, где она упрощает понимание кода.


ссылка на оригинал статьи https://habr.com/post/424067/