Contenuto
C++17 -> C++20 По традиции, рассказываю, что может пойти не так, когда вы хотите переехать с C++17 на C++20. В среднем вы не должны ничего заметить, но различий достаточно, чтобы заглянуть в страшные уголки плюсов. От самых страшных до не самых страшных 1. Spaceship operator<=> Единственная фича, которая уменьшила количество слов в стандарте и добавила головной боли компиляторным инженерам. Сломаться может интересно: если у типа есть implicit оператор вида struct Int { int i; operator bool() const { // Returns true if not 0, false if 0. return i; } }; Тогда <=> оператор в первую очередь применит оператор меньше к кастуемому типу, в итоге std::pair{Int{1}, Int{2}} < std::pair{Int{3}, Int{4}} вернёт 1 в C++17, 0 в C++20. Достаточно опасно, так как сильно меняет семантику. Как чинить? Писать explicit operator. Пример падения в проде. 2. operator== ambiguity struct B {}; struct D : B { bool operator==(const B& other); }; D d; bool eq = d == d; // работает в C++17, но не в C++20 На самом деле та же проблема. Бьярне и компания написали целую статью про это тут. Если коротко, то в C++20 написали правила, чтобы операторы == и != вели себя логичным образом, что !(a == b) это a != b. С другой стороны это создаёт проблему в духе struct S { friend bool f(S, const S&) { ... } friend bool f(const S&, S) { ... } }; bool b = f(S{}, S{}); И в итоге operator== и operator!= считаются одними и те же функциями. Это поломало много проектов, поэтому компиляторы быстро взяли фикс из C++23, и проблем больше нет https://godbolt.org/z/7e4E6qhbM. 3. Всякие проблемы с std::string_view против std::vector<std::string*like> и тд Конструкция {"a", "b"} вызывает лёгкий ужас, так как в C++20 в string_view добавили конструктор от 2 итераторов и "a" и "b" являются const char* start и const char* end, что очень плохо заканчивается: вы читаете чёрт пойми какую память. std::vector<std::string_view>{{"a", "b"}} Теперь создаёт палёный string_view из локации, где "a" -- начало, потом где "b" -- конец, а в C++17 создавало 2 строки. Санитайзеры не ловят. https://gcc.godbolt.org/z/jboa5rbfa. Самое ядро заключается в том, что {"a", "b"} создают не initializer_list, а обычную string_view Зато можно всякого интересного придумать. Например, можно добавить в libc++ код в духе template <int M, int N> constexpr basic_string_view(char_type (&a)[M], char_type (&b)[N]) : basic_string_view() { _LIBCPP_ASSERT(&a == &b); } И он будет падать в рантайме, в санитайзерах, орать warningами и быть ещё standard compliant. Выстрел в ногу, но всё таки элегантно чинится. 4. Всё остальное кажется достаточно минорным, атомики перестали быть trivially constructible, поэтому их в union не очень стоит класть, добавили слова concept, requires, добавились constexpr контейнеры, поэтому все шаблоны вектора должны быть полными типами. Стали делать std::move на начальное значение в std::accumulate, поэтому функция аккумулирования перестала принимать lvalue references. Aggregates теперь не компилируются, если есть конструкторы. Всё это чинится проходом по всему и достаточно прямолинейно. Я думаю по сложности C++14 -> C++17 был проще, чем C++17 -> C++20, но тоже со своими интересными решениями, как noexcept как часть типа и тд. Всё равно суммарно набирается работы, хоть и не так много. Самая интересная часть, которую пока никто не знает наверняка, насколько дольше стали компилироваться проекты.