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