Вступление
Доброго времени суток, друзья! Сегодня речь пойдёт об операторе 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/
Добавить комментарий