Spiiin's blog

Путеводитель С++ программиста по неопределенному поведению

Путеводитель C++ программиста по неопределенному поведению — увлекательное путешествие по тёмным уголкам C++.
(и еще пара ссылок ниже).

Автор несколько раз объясняет трудноуловимые ошибки в C++, через аналогичный код на Rust, в котором borrow-чекер находит проблемы. В каком-то виде, видимо, проверки lifetime когда-нибудь подъедут и в C++.

Один из первых примеров:

void f(float&& x) { std::cout << "float " << x << "\n";  }
void f(int&& x) { std::cout << "int " << x << "\n";  }
void g(auto&& v) { f(v); } // C++20
// template <class T> void g(T v) { f(v); }
int main() { 
    g(2);
    g(1.f);
}
// Выводит
// float 2
// int 1

void g(int&& v) {
    // Несмотря на то что тип v — int&&
    // Дальнейшее использование v в выражениях дает int& !
    // decltype(v)   == int&&
    // decltype((v)) == int&

    // Функции f принимают только rvalue ссылки

    // Неявное преобразование int& к int&& запрещено
    //  int&& x = 5;
    //  int&& y = x; // не компилируется!

    // Таким образом перегрузка f(int&&) не может быть использована

    // Остается f(float&&)
    // int умеет неявно приводиться к float
    // int& умеет неявно выступать в роли просто int
    // неявный static_cast<float>(v) возвращает временное значение float
    // временные значения типа T неявно биндятся к T&&

    // Имеем цепочку преобразований:
    // int& -> int -> float -> float&& 

    f(v); // будет вызван f(float&&) !

    // явно: f(static_cast<float>(v));
}

Семантический процесс приведения типов красиво стреляет в ногу (не так красиво как coersion в js, но и так неплохо получилось)

Константин Владимиров — Семантические процессы в C++
3 основных процесса:

- поиск и разрешение имён
- вывод и подстановка типов (проверка/приведение/вывод)
- инстанциирование шаблонов

Разобраться с каждым полезно для понимания языка. Кроме трюка с explicit для запрета каста в конструкторах с одним параметром и операторах преобразования, можно еще перекрыть преобразование явным удалением всех функций, кроме единственной:

int only_ints(int x) { return x;}

template <class T>
auto only_ints(T x) = delete; //отключение преобразования

Мой любимый пример даже не UB, а IFNDR — специализация шаблона не в заголовочном файле. Видел налёты на него раза три у знакомых программистов (последний раз в канале движка o2)
Магистерский курс C++ (МФТИ, 2022-2023). Лекция 3. Шаблоны классов и частичная специализация

Не копируйте! — фрик-статья о том, что ссылками в C++ сложно пользоваться (на самом деле, статьи с сайта можно давать на собеседованиях с вопросом “какие видишь логические ошибки в рассуждениях?”, и смотреть сколько кандидат насчитает).

Лучше всё-таки освоиться с Lifetime extension, чтобы представлять случаи, когда временный объект может прилипнуть к константной ссылке в неожиданном месте и ограничить время его жизни, так как продление жизни происходит один раз, и может быть использовано раньше, чем кажется, глядя на код.