Являются ли встроенные переменные уникальными по всем границам?
Это продолжение этого вопроса.
Как уже упоминалось в комментариях к ответу:
Встроенная переменная обладает тем свойством, что она имеет тот же адрес в каждой единицы перевода. [...] Обычно вы достигали этого, определяя переменную в файле cpp, но с помощью встроенного спецификатора вы можете просто объявить/определить свои переменные в файле заголовка, и каждая единица перевода, использующая эту встроенную переменную, использует точно такой же объект.
Более того, из самого ответа:
Хотя язык не гарантирует (или даже упоминает), что происходит, когда вы используете эту новую функцию на границах разделяемых библиотек, она работает на моей машине.
Другими словами, неясно, будет ли встроенная переменная гарантирована быть уникальной по всем границам при использовании разделяемых библиотек. Кто-то доказал, что он работает на некоторых платформах, но это не правильный ответ, и он может просто сломать все на других платформах.
Есть ли какая-либо гарантия относительно уникальности встроенной переменной, когда она используется через границы или это просто детали реализации, на которые я не должен полагаться?
Ответы
Ответ 1
Вот как я интерпретирую стандарт. Согласно basic.link/1:
Программа состоит из одной или нескольких единиц перевода, связанных друг с другом.
Он ничего не говорит о статической привязке или динамической компоновке. Программа представляет собой единицы перевода, связанные друг с другом. Не имеет значения, выполняется ли соединение в два этапа (сначала создайте.dll/.so, а затем динамический компоновщик соединяет все динамические библиотеки + исполняемые вместе).
Таким образом, в моей интерпретации не имеет значения, динамически или статически связана программа, реализация должна вести себя одинаково: статическая переменная класса должна быть уникальной (независимо от того, является ли она встроенной или нет).
В Linux это верно.
В Windows это не работает ни при каких обстоятельствах, поэтому в моей интерпретации он нарушает стандарт в этих обстоятельствах (если вы создаете отдельную.dll, содержащую статическую, не встроенную переменную и все остальные.dll и exe относится к этой переменной, она работает).
Ответ 2
C++ в настоящее время не имеет концепции разделяемых библиотек. Таким образом, поведение inline
в общих библиотеках будет implementation- и зависит от платформы.
Тот факт, что [basic.link]/1 гласит, что "Программа состоит из одной или нескольких единиц перевода, связанных друг с другом". не означает, что программа, связанная вместе с другим, уже связанным модулем, должна вести себя одинаково.
За эти годы было подано много предложений, чтобы исправить ситуацию (N1400, N1418, N1496, N1976, N2407, N3347, N4028), ни одна из которых не сбилась с места. Его просто сложно реализовать в общих чертах, а C++ обычно пытается избежать деталей реализации. Как выразился GCC:
Для целей, которые не поддерживают COMDAT или слабые символы, большинство объектов с неопределенной связью испускаются как локальные символы, чтобы избежать дублирования ошибок определения из компоновщика. Однако это не происходит для локальной статики в строках, однако, поскольку наличие нескольких копий почти наверняка ломает ситуацию.
MSVC по умолчанию не предоставляет никаких символов. Любой "внешний" символ должен быть явно объявлен с помощью специфичного для платформы __declspec(dllexport)
. Из-за этого нельзя утверждать, что Windows несовместима с C++. Ни один из C++ правил не нарушен здесь, потому что их нет.
Ответ 3
Есть ли какая-либо гарантия относительно уникальности встроенной переменной, когда она используется через границы или это просто детали реализации, на которые я не должен полагаться?
Для вас это необходимо (убедившись, что все объявления на самом деле одинаковы).
Компилятор, очевидно, не может проверить это, и компоновщик не беспокоится. Поэтому, если вы лжете линкеру (не делая выше), тогда у вас будут проблемы.
Хорошо, так как не все получают то, что я имею в виду под "ложью к компоновщику", я немного поразмычу.
@oliv любезно предоставил эту ссылку, которая, между прочим, говорит об этом (комментарий мой):
Повторяющиеся копии этих конструкций [т.е. переменные объявляются inline в нескольких TU] будут отброшены во время ссылки.
Это прекрасно, что нам нужно. Дело в том, что вы не знаете, какие из них (очевидно, только один из них сохранен, поэтому, по расширению, вы не знаете, какой из них будет).
Итак, если они отличаются друг от друга, вы не знаете, в какой из них вы закончите, и поэтому в итоге вы получите (особенно коварную форму) UB. Это то, что я имел в виду под "ложью линкера". Потому что, объявляя свои переменные по-разному в разных ТУ, это именно то, что вы сделали. Упс!