Какой объектный файл содержит следующую статическую шаблонную "переменную-член"?
Допустим, у меня есть следующий класс шаблона со статической функцией-членом, которая сама создает экземпляр статической переменной (которая функционально является статической переменной-членом, создаваемой при первом вызове содержащей ее подпрограммы):
template <typename T>
struct foo
{
static int& mystatic()
{
static int value;
return value;
}
};
Если я использую foo<T>
в нескольких единицах перевода для некоторых T
, в какой объектный файл компилятор помещает foo<T>::mystatic::value
? Как это явное дублирование/конфликт разрешается во время соединения?
1 ответ:
Вы понимаете, что ваша функция
mystatic
является функцией с внешней связью? Это означает, что один и тот же конфликт существует между несколькими определениямиmystatic
, сделанными в разных единицах перевода. Кроме того, точно такая же проблема может возникнуть и без шаблонов: обычныеinline
функции с внешней связью, определенные в заголовочных файлах, могут привести к тому же очевидному множественному конфликту определений (и та же проблема с локальной статической переменной может быть воспроизведена там как хорошо).Чтобы разрешить такие конфликты, все такие символы помечаются компилятором некоторым зависящим от реализации способом. Делая это, компилятор сообщает компоновщику тот факт, что эти символы могут быть юридически определены несколько раз. Например, одна известная реализация помещает такие символы в отдельный раздел объектного файла (иногда называемый разделом COMDAT). Другие реализации могут обозначать такие символы каким-либо другим способом. Когда компоновщик обнаруживает такое символы в нескольких объектных файлах вместо того, чтобы сообщать об ошибке множественного определения, он выбирает один и только один из каждого идентичного символа и использует его во всей программе. Остальные копии каждого такого символа отбрасываются компоновщиком.
Одним из типичных следствий этого подхода является то, что ваша локальная статическая переменнаяvalue
должна быть включена в качестве внешнего символа в каждый объектный файл, несмотря на то, что она не имеет связи с точки зрения языка. Имя символа обычно состоит из имени функцииmystatic
и имени переменнойvalue
и некоторых других искажений. Другими словами, компилятор сам помещает определениеmystatic
и переменнуюvalue
в все независимые объектные файлы, использующие функцию-член. Компоновщик позже убедится, что в связанной программе существует только одинmystatic
и только одинvalue
. Вероятно, нет способа определить, какой исходный объектный файл предоставил оставшийся в живых копировать (если такое различие вообще имеет смысл).