Есть много вопросов по ODR, но я не могу найти то, что ищу, поэтому извиняюсь, если это дубликат или название неуместно.
Учтите следующее:
struct t {t(*id)();};
template<typename T>
t type() {return {type<T>};}
Это чрезмерное упрощение моей попытки определить уникальный идентификатор для каждого типа, мы надеемся, что он остается уникальным для разных модулей компиляции.
В частности, учитывая конкретный тип T
лайк std::string
и, предполагая, что два различных блока компиляции включают в себя код выше в заголовочном файле, я хотел бы выражение
type<T>().id
принять одинаковое значение (типа t(*)()
) в обеих единицах, следовательно, служат уникальным идентификатором для типа T
,
Значение является адресом функции type<T>
, поэтому вопрос в том, является ли уникальная функция type<T>
в программа гарантируется правило одного определения. ISO 3.2 / 3 говорит
Каждая программа должна содержать ровно одно определение каждой не встроенной функции или переменной, используемой в этой программе.
где по 3,2 / 2
Неперегруженная функция, имя которой появляется в качестве потенциально вычисляемого выражения или […], используется odr, если […]
и я предполагаю, что функция не является встроенной, если ее адрес взят (хотя я не могу найти это в стандарте).
ISO 3.2 / 5 перечисляет ряд исключений, но единственные ссылки на функции
встроенная функция с внешней связью, […], нестатический шаблон функции, […], функция-член шаблона класса или специализация шаблона, для которой не указаны некоторые параметры шаблона […]
и ни один, кажется, не имеет место здесь.
Поддающийся проверке пример может занять более одного файла. На самом деле, пример, заявленный как провал, дается Дитер Люкинг, хотя это не терпит неудачу в моем случае (который я не принимаю как любую форму «гарантии»).
Так это сработает или нет?
Так что 3.2 / 5 на самом деле кажется довольно сильной поддержкой. Прежде всего обратите внимание, что определение является конструкцией исходного кода, а не конструкцией объектного кода, хотя очевидно, что существует очень тесная связь. 3.2 / 5 говорит, что можно иметь несколько определений шаблонов нестатических функций, и, кроме того, в таком случае оно должно вести себя так, как если бы было только одно определение. Если функция имела разные адреса в разных единицах перевода, то это не ведет себя так, как если бы было только одно определение, по крайней мере, в моем чтении.
Это особенно верно, поскольку указатель на функцию может быть передан в качестве нетипового аргумента шаблона. Такие аргументы должны быть постоянными и должны быть одинаковыми для всех единиц перевода. Например, строковый литерал не может быть таким аргументом именно потому, что его адрес варьируется в зависимости от единицы перевода.
Будут ли выполнены все требования, будет зависеть в точности от контекста нескольких определений, поскольку они имеют дело с такими вещами, как разрешение имен и т. Д. Однако все они являются «заурядными» требованиями, которые типа «конечно». Например, нарушение было бы что-то вроде:
file1.cpp
static int i;
// This is your template.
template <typename T>
void foo() {
i; // Matches the above i.
}
file2.cpp
static int i;
// This is your template. You are normally allowed to have multiple
// identical definitions of it.
template <typename T>
void foo() {
// Oops, matches a different entity. You didn't satisfy the requirements.
// All bets are off.
i;
}
Я знаю, что множественные определения поддерживаются в Linux через слабые символы. На самом деле, в Linux пример Lucking не может провалиться именно из-за этого. Я оставил комментарий к его ответу с просьбой о платформе. Во время ссылки компоновщик отбрасывает все экземпляры слабого символа, кроме одного. Очевидно, что если экземпляры на самом деле не совпадают, это было бы плохо. Но эти требования в 3.2 / 5 предназначены для того, чтобы гарантировать, что экземпляры фактически все одинаковы, и, следовательно, компоновщик может оставить только один.
ДОБАВЛЕНИЕ: Дитер Лакинг теперь говорит, что у него была проблема с компиляцией, и это на самом деле для него не проблема. Было бы хорошо, если бы кто-то, знакомый с внутренними компонентами Windows DLL, мог прокомментировать здесь, как Visual Studio справляется с этим.