Оказывается, чтобы получить неопределенное поведение даже необязательно писать какой-то плохой код. Достаточно просто не добавить перенос строки в конце подключаемого файла!
Небольшой пример:
Файлик foo.hpp:
// I love code
// I love C++<no newline>
Файлик bar.cpp:
#include "foo.hpp"
#include "baz.hpp"
А теперь вспоминаем, что препроцессор вставляет все содержимое хэдера на место инклюда И(!) не вставляет после него символ конца строки. То есть спокойно может получится следующее:
// I love code
// I love C++#include "baz.hpp"
То есть включение baz.hpp может быть полностью заэкранировано.
Учитывая, сколько всего препроцессор может делать с кодом, комбинации вариантов развития событий могут быть абсолютно разными.
Стандарт нам говорит:
... If a source file that is not empty does not end in a new-line character,
or ends in a new-line character immediately preceded by a backslash
character before any such splicing takes place, the behavior is undefined.
Так что ub без кода - вполне существующая вещь.
Или уже нет?
На самом деле приведенная цитата была из стандарта 2003 года.
С++11 пофиксил эту проблему и обязал препроцессоры вставлять new line в конце подключаемых файлов:
A source file that is not empty and that does not end in a new-line character,
or that ends in a new-line character immediately preceded by a backslash
character before any such splicing takes place, shall be processed
as if an additional new-line character were appended to the file.
Так что теперь проблемы нет.
Решил написать об этом, просто потому что очень весело, что в плюсах можно было такими неочевидными способами отстрелить себе конечность.
Ну и хорошо, что стандарт все-таки не только новую функциональность вводит, а фиксит вот такие вот недоразумения.
Fix your flaws. Stay cool.
#compiler