разглядывая код некоторых опенсорсных проектов хочется разбить себе лицо
Странная логика. То, что некоторые программы на $langname написаны плохо, не значит, что на этом же $langname нельзя писать хорошо. А так-то, программу на фортране можно написать на любом языке..
static, потому что с дефолтными настройками видимости символов если вы подключите этот файл с двух местах то компилятор вам скажет «дядя, этот символ уже есть в другом месте».
Если сомневаешься и оно само так вышло, значит ты ещё не готов к тому, чтобы мотивировано добавлять логику/реализацию в заголовочных файлах. Следуй рекомендации о разделении интерфейса и реализации.
P.S. Когда дорастешь до сеньора пограмиста, то узнаешь что можно создавать целые фреймворки чисто на шаблонах в hpp-файлах.
когда в разных compilation units один и тот же тип имеет разный размер
Размер может и сохранятся, достаточно что бы смещения внутри структуры поплыли
struct A{
#ifdef XXX
int x; // вчера ты не дал мне сигарету?
#endif
int y;
#ifndef XXX
int x; // я еще Makefile подшаманил, приятной отладки, бро!
#endif
};
Линкер тупой. Он смотрит - есть объект, в нем поле x имеет тип Т и смещение off, так к нему и обращаемся. Ликёр наивно считает что во всех обьектниках эта информация одинакова. Если информация разная то линкер ни за что не отвечает - можно попасть за пределы объекта (как в варианте 1) или в другое поле (как в варианте 2).
Молодец, верно отвечаешь, типы здесь действительно разные. И уж тем более, это не соответствует теме писать код в .h или в .c.
Проблема здесь в том, что линкер эти типы никак проверять не может - типов нет на уровне линкера. Вопреки утверждениям выше о том, что линкер смотрит в типы: мог бы он смотреть в типы - он бы выдал ошибку о несоответствии.
Поэтому нужно руками гарантировать эквивалентность того, что передаётся между разными tu. Вот тот самый odr частично пытается этому способствовать, выдавая ошибку на переопределение идентификатора в рамках одного tu. Т. е. сам odr существует только в рамках tu, между разными tu odr уже нет.
Вопреки утверждениям выше о том, что линкер смотрит в типы: мог бы он смотреть в типы - он бы выдал ошибку о несоответствии.
Линкер с LTO ругается на несоответствие типов.
C++ ABI позволяет сохранять типы, частично.
ODR также касается определению имён между единицами трансляции. Что может быть несколько определений у структур/классов в единицах трансляции, но определения должны совпадать.
Выше нигде нет lto. Как и нет никакого смысла его использовать.
C++ ABI позволяет сохранять типы, частично.
Не позволяет. Кстати, частично - значит уже не позволяет в целом.
ODR также касается определению имён между единицами трансляции. Что может быть несколько определений у структур/классов в единицах трансляции, но определения должны совпадать.
Не касается. Нет, совпадать безусловно они не должны. Я написал, при каких условиях нужно совпадение.
Я описал чем является odr в реальности(в контексте темы). Какие-то пункты ничего не значат. По крайней мере, ты должен из этих пунктов вывести обоснование того, о чём пытаешься говорить.
h - прототип Cpp - реализация. Но у меня в h есть логика. Это очень плохо?
У такого подхода есть только один плюс: компилятор сможет инлайнить код из .h по месту использования. Но с этим, как и с любой оптимизацией, нужна осторожность, так как инлайнинг раздувает размер бинарников, а поможет ли он что-нибудь ускорить, зависит от обстоятельств. К тому же если использовать LTO, то ничего выносить в заголовки уже не требуется, т.к. компилятор видит код всего бинарника сразу.
А минусов полно, с некоторыми можно бороться (например, разделить прототипы и реализацию на header.h и header_impl.h), с некоторыми нет (при любом изменении в «логике» придётся перекомпиливать всё, куда инклудится заголовок)