Зачем нам использовать Modern C++, особенно старших версий? Дурацкий вопрос, скажут сектанты, фанаты, фанатики, адепты. Оно же необходимо для большей выразительности, читабельности и ускорения всех возможных процессов, включая метаболизм; и еще для экономии времени. Что-то в этом есть, а кое-чего нет. Давайте рассмотрим пример из жизни.
Один программист (имя сохранено в редакции) писал себе спокойно код и написал примерно такое (для справки: используемый стандарт 2017):
#include <vector>
int main() {
std::vector<int> vector;
vector.push_back(0);
vector.push_back(0);
vector.push_back(0);
//здесь давайте на секунду представим себе, что содержимое вектора заранее неизвестно и он вполне может быть пустым
int x=0;
for(auto& n:vector){
x++;
//Сделай что-нибудь!
}
return 0;
}
Написал, сладко потянулся и отправил в билд.
К слову, билд штука непростая, состоит из многих этапов и занимает примерно час+.
Всего через 30 минут он обнаружил, что билд не удался. Дело в том, что у него на работе билд включает в себя прогон линтера. Линтер, жестокий и беспощадный, с ухмылкой сообщил, что обнаружил ошибку, мимо которой он не может пройти спокойно и просто обязан выполнить свой гражданский долг.
Стали разбираться.
Ошибка оказалась следующей: линтер обнаружил, что при определенном стечении обстоятельств переменная х может оказаться неиспользованной. Ну то есть, когда вектор совсем пустой. Ахтунг!
Оказывается, в range-based for loop нельзя затащить локальную переменную, как в обычном цикле. Ну то есть можно, но об этом ниже.
Получив письмо счастья, автор сел разбираться.
И пришел к выводу, что есть несколько способов решить проблему:
1. приказать линтеру заткнуться и не вякать (хаха, смешно; то есть это осуществимо, но не принято).
2. после цикла эту несчастную переменную аннулировать, например выражением (void)x или что-нибудь в этом духе.
3. написать классический цикл, задать локальную переменную - как деды завещали.
4. перейти на старшую версию c++ (хахаха, гомерический хохот, долгие, несмолкающие аплодисменты)
5. или попытаться добавить локальную переменную в range-based for loop, как-то так:
for(int x=0; auto& n:vector)
{
x++;
//Сделай что-нибудь!
}
Тут правда есть небольшая засада: такой маневр допустим только в версиях 2020 или 2023. И то, нет гарантии, что все, или хотя бы ведущие компиляторы его поддерживают. MSVC по крайней мере нет.
Лирическое отступление
Итого, что получается?
В язык затащили новую фичку. Но она оказалась малость недоделанной и всего спустя декаду ее таки доделали, но не все производители компиляторов об этом узнали.
И это уже не очень здорово, потому что если посмотреть, в какой степени компиляторы поддерживают разные версии стандарта, то можно загрустить:
ссылки:
https://en.cppreference.com/w/cpp/compiler_support/11.html
https://en.cppreference.com/w/cpp/compiler_support/14.html
https://en.cppreference.com/w/cpp/compiler_support/17.html
https://en.cppreference.com/w/cpp/compiler_support/20.html
https://en.cppreference.com/w/cpp/compiler_support/23.html
https://en.cppreference.com/w/cpp/compiler_support/26.html
Обратили внимание? Даже ведущие компиляторы, уже начиная со стандарта 2017 (а по факту даже с 2011) не гарантируют полную совместимость со стандартом как по самому языку, так и по стандартной библиотеке. И дальше расхождений все больше - в 2020 даже gcc, до этого бывший огурцом, начал уставать.
И даже друг с другом совместимость не гарантируют.
И с каждым новым стандартом все меньше производителей компиляторов собираются поддерживать эту муть.
Попросту говоря, если вы пишете код под стандарт 20хх под конкретный компилятор - не факт, что вы сможете скомпилировать его под другим компилятором! И это еще до того, как мы начала говорить о различиях в железе.
В итоге по-видимому через стандарт-другой мы останемся с парой-тройкой компиляторов, а то и вообще с одним, самым упрямым, который будет поддерживать пол-стандарта.
Например, стандарт 26 целиком не реализует вообще никто. Даже наполовину не. Даже стандарт 23 никто целиком не реализует.
А ведь планируется уже стандарт 29.
Тут уместно задать вопрос: а зачем вообще плодить стандарты которые реализуются через пень-колоду?
Но это наверно риторический вопрос, который невозможно расслышать через бой бубнов.
Казалось бы, что в этом плохого?
А то, что другие производители либо погибнут, либо уйдут в свои ниши.
В результате получится много совершенно непортабельного и неподдерживаемого кода.
Не то, чтобы раньше его не было - но раньше он происходил в основном из-за различий в железе.
Теперь к этому добавятся различия в стандартах и невежественные требования "сделать красиво", то есть модно, молодежно, читабельно и выразительно.
Но разве это остановит комитет? Нет, он будет продолжать кипучую деятельность, их бешеный принтер имеет большие творческие планы. A кучка упоротых фанбоев будет бегать вокруг с бубнами, выкрикивать дурацкие лозунги про прогресс и легаси и создавать 99% хайпа.
И это примерно то, что происходит.
Мне кажется, комитет совершенно потерял связь с целевой аудиторией, то есть с реальностью.
Почему-то вспомнилось название одной малоизвестной книги - "Пациенты рулят психбольницей."
Успехов всем нам.
Вернемся в исходную историю.
Как же наш герой справился с проблемой?
Во общем, эту проблему парень решил, неважно как (как-как: перешел на стандарт 2023! Хахаха, люди задыхаются от смеха и сползают с кресел!) Просто написал нормальный классический цикл.
Я бы хотел заострить внимание на другом.
Человек мог написать этот код по дедовским рецептам и все прокатило бы.
Но он решил, что стоит написать по-модному, выразительно и читабельно. К сожалению, он не учел, что прежде чем его код будут читать коллеги, то же самое попытается сделать проклятый линтер. В результате первый билд пошел прямо в мусорку - 30 минут коту под хвост.
Затем минут 15 он выяснял где накосячил и как чинить.
Еще минут 30 он чинил - потому что мало починить, желательно еще локально скомпилировать и запустить, чтобы убедиться что все в порядке, а это занимает время.
И потом еще раз запустить билд.
Итого: за следование моде он заплатил более чем часом работы.
Finale
Выводы каждый пусть делает сам.
P.S. Этот текст был создан без использования БЯМ.
Комментарии (15)
Abstraction
26.08.2025 10:50В язык затащили новую фичку. Но она оказалась малость недоделанной
Что недоделанного в самой фиче? Она работает как и обещалось, возможности объявлять что угодно в области видимости цикла нет, её и раньше не было.
Я бы сказал "схрена ли такое правило линтера", но и правило разумное: если вы никак не используете
x
вне цикла, то что он вообще делает? Если это хитрое преобразование коллекции с аккумулятором, то правило линтера намекает вынести его в отдельную функцию (или воспользоватьсяstd::transform
):static void TransformData(std::vector<int>* data){
int x = 0;
for(int& elem : *data){
elem = x += elem;
}
}
(Если линтер и в этом случае считает код нарушением, то ИМХО лучше написать явное исключение, чемif(data->empty()) return;
)Наконец, при 30-минутном билде может иметь смысл прогонять линтер локально, чтобы такие штуки не сваливались на голову сюрпризами?..
sergey_prokofiev
26.08.2025 10:50Сначала была одна крайность: C++0X рожали лет 10 и так и не сумели выпустить в 0X годах.
Затем другая крайность: по небольшому стандарту, но каждые 2 года.
Наверное следующая итерация будет "вменяемый баланс".
eao197
26.08.2025 10:50Затем другая крайность: по небольшому стандарту, но каждые 2 года.
Назвать C++20 небольшим стандартом как-то сложновато.
Да и грядущий C++26 тоже маленьким не назовешь...
apevzner
26.08.2025 10:50Но он решил, что стоит написать по-модному, выразительно и читабельно. К сожалению, он не учел, что прежде чем его код будут читать коллеги, то же самое попытается сделать проклятый линтер. В результате первый билд пошел прямо в мусорку - 30 минут коту под хвост.
А что, нельзя было прогнать линтер локально и до более долгосрочных этапов сборки?
dayman092
26.08.2025 10:50Да, можно прогнать линтер локально на проектах уровня хелловорлд+, но даже с 10-ти летним аосп это не выйдет и я не знаю, как там прогнать линтер отдельно. И зачем тратить время на поиски этой информации?
По-моему с++ скатился где-то на std::chrono и полностью превратися в Г при появлении корутин, все их пишут, куча реализаций, прироста с CSP почти никакого, проекты, в итоге, становятся неподдерживаемыми.
Ещё мне нравилось работать с базовыми типами, сейчас развели целый зоопарк, умножим, на шаблоны, которые раздувают объектный код, в итоге всё это работает и разрабатывается медленно.
eao197
26.08.2025 10:50Даже ведущие компиляторы, уже начиная со стандарта 2017 (а по факту даже с 2011) не гарантируют полную совместимость со стандартом как по самому языку, так и по стандартной библиотеке.
Это моя реальность начиная с 1990-х годов, еще даже до принятия первого стандарта -- у каждого компилятора было свое подмножество (а в бородатых 90-х, порой, и нестандартное надмножество) C++.
Таков путь.
Прямое следствие того, что есть отдельно стандарт и есть несколько независимых вендоров компиляторов.
Если когда-нибудь все компиляторы кроме одного единственного помрут, ситуация и поменяется, может быть. Но пока этого не произошло, будет именно так.
Жить с этим можно. А страдают от такого, в основном, разработчики кросс-платформенных библиотек и кросс-платформенных приложений. Если же проект живет в рамках одной платформы и одного компилятора (а таких, как оказалось, немало), то и проблем особых нет.
Cheater
26.08.2025 10:50переменную аннулировать, например выражением (void)x
(Void) не способен аннулировать свой операнд, ни один оператор приведения типа это не может. Это ничего не делающая операция, древний хак для обхода предупреждения компилятора о неиспользованной переменной.
NeoCode
26.08.2025 10:50Уверен в том, что С++ нужно прекратить развивать тем способом как это делается сейчас (добавлением новых фич с сохранением обратной совместимости со старыми). Нужно ввести версии языка, и в новой версии просто удалить всю ту фигню которую нагородили эволюционно, и добавить новые фичи вообще не оглядываясь на обратную совместимость, а думая лишь об их логичности, удобстве, красоте. Чтобы компилятор оличал исходники разных версий в одном проекте, в начале файла писать #pragma version. Если прагмы нет - значит "старая" (т.е. текущая) версия.
Videoman
26.08.2025 10:50Звучит заманчиво, самому бы хотелось решения с помощью подобного варианта, но возникают вопросы. Допустим у нас компилятор с помощью опций или директив поддерживает старую и новую версию С++. Мы можем писать код полностью на новой версии или на старой версии - ура! Но если мы хотим поддерживать несколько версий одновременно, значит мы планируем какой-то постепенный переход. А как в этой ситуации делать interop ? ABI сломано, одни и те же конструкции теперь могут означать совсем разное. Через "С" ?
Jijiki
26.08.2025 10:50на первый взгляд звучит уверенно и вроде логично, но какой ценой ... что-то там, С++ не плохой по моим прикидкам
XViivi
26.08.2025 10:50[[maybe_unused]]
добавили в C++17. Даже до этого, если нет согласия с линтером и есть достаточная уверенность, что он брешет, вариант с(void)
или любой другой подобной глушилкой был бы не так плох.Собственно, я впринципе с точки зрения линтера не особо понимаю, в чём проблема, если переменная не использовалась в одной из веток — важнее же чтобы использовалась хотя бы в одной, разве нет?
BombaBomba
26.08.2025 10:50"К слову, билд штука непростая, состоит из многих этапов и занимает примерно час+." Если следовать заголовку, то должно быть час++
pavlushk0
26.08.2025 10:50Тейк про линтер непонятен, у вас clang daemon в ci или к иде не прикручен? Новые стандарты поддержаны неполно, но от этого не становятся бесполезными. Нишевого проходняка много, конечно, но концепты, корутины и модули это отлично, start lifetime as тоже супер, рефлексия вот подошла, всякого полно. А про то что язык большой это уже данность, его никто целиком не знает, дпже если делает вид что знает не верьте) P1787 тому докозательство.
arteast
26.08.2025 10:50Судя по всему,
x
- это индекс элемента в коллекции. Поэтому вариант номер 6 - выразить это явным образом: использоватьenumerate
изrange-v3
или `boost::adaptors::indexed
изboost-range
+ structured bind.
BorisU
у вас кривой линтер и вы не умеете запускать его без компиляции всего. Язык-то то тут причем?