Как стать автором
Обновить

Комментарии 27

Интересный пример. Я даже записал эго в список паттернов, который я создаю впрок для последующей реализации в PVS-Studio. Сейчас анализатор не производит межмодульный анализ. Но возможно через несколько версий мы и до него доберемся. Ресурсов маловато.

Вопрос не в тему. Вы используете что-то из инструментов статического анализа при разработке?

Да, /W4 в сочетании c /WX помогает найти кучу проблем. Например, C4930 — такой дефект легко посадить, не так-то просто заметить, а последствия крайне неприятные.
Вы поставили ссылку рекурсивно сюда же. Хотели поставить ссылку сюда?
Да. Cпасибо.
Здравствуйте! А уже добрались или еще нет?
Нет, но не по техническим причинам, а из-за сложностей сценариев использования. Много вопросов по инкрементальному анализу, где хранить базу информации по другим модулям, что делать если ещё базы нет, а человек хочет проверить один файл. И т.д. Вопросов очень много. Подходите к нам на стенд на одной из конференций, пофилософствуем.
Хороший пример. Повод ещё раз задать себе вопрос — а нужен ли мне C++ при разработке.
Это очередное подтверждение известного тезиса о том, что C++ даёт исключительное количество способов выстрелить себе в ногу.

Другой вопрос, что C++ сам по себе не такой уж плохой язык (в чём я уверен, пользуясь им изо дня в день в течение многих лет). Так что стоит задать себе вопрос «Почему именно мне не нужен C++ в разработке?» и постараться самому себе на него честно ответить.
Я себе на этот вопрос давно ответил. Ответ простой — «У меня нет задач в которых С++ оправдан».

Нет у меня сейчас ни одной низкоуровневой задачи для которой не хватит возможностей С и ни одной достаточно высокоуровневой, в которой не хватит производительности .NET/python

В дополнение к этому ответу можно ещё сказать «я ленив». Одну и ту же задачу на C++ и C# я на C# реализую быстрее и решение будет иметь _достаточную_ производительность для его работы. А разницу во времени я потрачу на что-нибудь нужное мне.

Где можно почитать о серьёзных преимуществах C++ перед C# например, а не блеянье о «многа производительно-ма» и «Так памяти многа кушать будета-ма».

Для себя я вижу очень ограниченный круг задач в которых C++ будет необходим — это геймдев, Highload, графические приложения, т.е. те области, где действительно необходима максимальная производительность и тонкая работа с ресурсами вообще и с памятью в частности, а процедурное представление на С приведёт к слишком большой непонятности кода.

Короче, вместо дедовской опасной бритвы, которая отлично бреет, но которой можно и зарезаться, я выбираю безопасный Gilette, которым я смогу побриться, но не смогу нарезать хлеб.
Спасибо за ссылку.

Судя по комментариям, всё сводится к тому же, что инструмент выбирается под задачу.

lambda-the-ultimate.org/node/663#comment-5702 — очень близок к моей точке зрения

блин, рано отправил :(

lambda-the-ultimate.org/node/663#comment-5758 тоже хороший комментарий, в котором чётко сказано, чего не хватает в других языках. Но, комментарий 2005 года, когда C# был еще совсем никакой. Из того, что перечислено в списке я не знаю разве что про memory mapped structures.
А нужен ли мне молоток? Ведь им можно легко пробить себе голову. Буду вкручивать гвозди отверткой. Хотя и в отношении последней не все гладко
> Например, в двух разных заголовках могут случайно оказаться разные inline функции с одной и той же сигнатурой.
А как такое возможно?
Простое совпадение — в одном заголовке один разработчик написал функцию с какой-то сигнатурой и каким-то именем, в другом другой разработчик — другую функцию с теми же сигнатурой и именем. Это крайне маловероятно, если использовать разумный подход к выбору имен функций, но тем не менее возможно.
Мда, вы жалуетесь не на то, что нужно.

Компилятору должно быть глубоко по барабану что у вас там за пределами текущего модуля. Это не его задача, и кроме пожалуй ICC, который осиливает меж-модульную оптимизацию (IPO) я не припомню таких способностей.
Примечание — IPO сильно ухудшает время компиляции.

Дальше насчет множественных символов. Я не скажу про Win/VC, но под линуксом по-умолчанию у линкера стоит «не пущать». Есть обратный ключик «muldefs» который разрешает подобные извращения.

Про то, какая функция попадет. Все вообщем то известно. Линкер (если у вас нет групп) делает однопроходную линковку слева направо. И символ который будет правее заменит предыдущий.
Линкер (если у вас нет групп) делает однопроходную линковку слева направо. И символ который будет правее заменит предыдущий.
Это особенность реализации конкретного линкера, и никто не гарантирует, что в следующей версии будет точно так же. Не стоит на такое полагаться.
-и никто не гарантирует
Конечно никто не гарантирует. Однако если у вас в проекте уже имеется такой косяк, как нескольо одинаковых экспортруемых символов, то полагаться вам больше не на что.
Вся кухня с выявлением множественных символов работает только для не-inline функций. К inline функциям эта кухня не применяется. Об этом первые четыре абзаца. Так что да, вы правы насчет множественных символов, межмодульной оптимизации и ключей типа muldefs, но все это не имеет никакого отношения к предмету поста.

Более того, комментарий ниже, в общем полностью справедлив, но не очень полно сформулирован. Если о какой-либо ситуации в стандарте говорится «поведение не определено», то все попытки «объяснить», «доказать» или «обосновать» действия компилятора и поведение программы обречены — в лучшем случае после кропотливого анализа всех исходников конкретного компилятора и C++ runtime вы сможете сделать вывод о конкретной версии компилятора и C++ runtime. Эти выводы будут справедливы только для этой версии. Стоит обновить компилятор или runtime или перейти на другой компилятор — и «анализ» нужно проводить заново. Естественно, код с неопределенным поведением автоматически оказывается непереносимым между разными компиляторами и разными реализациями C++ runtime. Оно того не стоит.
inline функции обычно определяются в заголовочных файлах (.h), чтобы все единицы трансляции могли видеть реализацию функции и подставить ее по месту вызова. Соответственно, как только вы включите заголовочный файл с такой функцией в более чем одну единицу трансляции, ODR будет формально нарушено, но… никакой индикации ошибки вы не получите.

Это не нарушает ODR.
Consequently, the rule in the standard that says that there must be a unique definition of a class,
template, etc., is phrased in a somewhat more complicated and subtle manner. This rule is com-
monly referred to as ‘‘the one-definition rule,’’ the ODR. That is, two definitions of a class, tem-
plate, or inline function are accepted as examples of the same unique definition if and only if

[1] they appear in different translation units, and
[2] they are token-for-token identical, and
[3] the meanings of those tokens are the same in both translation units.

B.Stroustrup, The C++ Programming Language, 3rd. ed., P. 9.2.3

Будет ли это работать? Какая реализация HandleErrorCondition() – с вызовом _exit() или с вызовом CustomHandleErrorCondition()– будет вызываться?

Неизвестно.

Как это неизвестно? Вполне даже известно.
SomeUsefulFunction в staticlib.obj во время линковки будет заменён на аналогичный символ из example.obj; почему — описано в комментарии DustCn. Таким образом из обоих модулей будет вызываться реализация определённая в example.obj, которая, в свою очередь, будет вызывать реализацию HandleErrorCondition с CustomHandleErrorCondition.
При линковке возникает описанная выше ситуация с нарушением ODR, но теперь две inline функции, идентичные с точки зрения политики, используемой линкером, имеют разные реализации. Линкер выберет какую-то одну и не факт, что выбор не будет меняться от одной линковки к другой.

Это нарушение стандарта, а не ODR. Поскольку inline-функции с одинаковой сигнатурой должны иметь одинаковые реализации в различных единицах трансляции. И то, что пример удачно компилируется, является скорее отступлением компилятором MSVC от стандарта.

Чтобы избежать подобных неоднозначных ситуаций стоит делать заголовочные файлы настолько автономными, насколько это возможно.

Вместо послесловия:
It is wise not to be too clever about the use of #include. My recommendation is to #include
only complete declarations and definitions and to do so only in the global scope, in linkage specifi-
cation blocks, and in namespace definitions when converting old code. As usual, it is wise
to avoid macro magic. One of my least favorite activities is tracking down an error caused by a
name being macro-substituted into something completely different by a macro defined in an indi-
rectly #include'd header that I have never even heard of.

B.Stroustrup, The C++ Programming Language, 3rd. ed., P. 9.2.1
Это нарушение стандарта, а не ODR. Поскольку inline-функции с одинаковой сигнатурой должны иметь одинаковые реализации в различных единицах трансляции. И то, что пример удачно компилируется, является скорее отступлением компилятором MSVC от стандарта.

Это не нарушение стандарта и не отступление от стандарта. Стандарт говорит, что в этом случае поведение не определено. Соответственно, никаких ограничений не накладывается.

Все попытки «объяснить» неопределенное поведение — пустая трата времени.
Да. Там говорится «должны выполняться вот эти условия». То условие, что вы подчеркнули, первое в списке. Дальше идут еще несколько условий. В самом конце параграфа говорится «Если определения D удовлетворяют всем этим требованиям, программа должна вести себя так, как если бы сущность D была определена один раз. Если определения D не удовлетворяют этим требованиям, поведение не определено.»

Именно о последнем предложении и говорится в посте. Определения D не удовлетворяют тем требованиям, поэтому поведение не определено.

Действительно. Прошу прощения, стандарт читал бегло, и, увидев, что начинается с примера с шаблонами не стал дочитывать до конца. Предыдущее же утверждение целиком опиралось на Страуструпа.
Да, совершенно верно. Но стандарт говорит об этой ситуации, что поведение не определено.

Другой пример — разыменование нулевого указателя ({int* ptr = 0; *ptr;}). Можно ли сказать о разыменовании нулевого указателя «it isn't just bad taste, it is illegal»? Можно. Стандарт говорит, что при разыменовании нулевого указателя поведение не определено. Если реализация в этом случае сожжет ваш компьютер и потратит все ваши деньги, она все равно будет считаться соответствующей стандарту.

То же самое и с inline функциями. Поведение реализации в этой ситуации не определено. Речь не о том, что тот код правильно написан, речь о том, что эта ситуация в соответствии со стандартом приводит к неопределенному поведению, возможны серьезные проблемы и даже реализация компилятора/линкера, полностью соответствующая стандарту, вас не спасет.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий