Должны ли статические локальные переменные специализаций шаблонов функций с T=‹безымянным классом в пространстве имен› быть уникальными?

Мы используем компилятор Intel C++ и обнаружили, что он неправильно компилирует (?) следующее, уменьшенное из-за использования boost::function<Ponies()> f(unnamedNamespacedFunctor).

a1.cc:

template<typename T>
int f(T) { static int x = T::x; return x; }


namespace { struct A { static const int x = 1; }; }

int f1() {
   return f(A());
}

a2.cc:

template<typename T>
int f(T) { static int x = T::x; return x; }


namespace { struct A { static const int x = 0; }; }

int f2() {
   return f(A());
}

основной.cc:

#include <cstdio>

int f1();
int f2();

int main() {
   std::printf("%d != %d\n", f1(), f2());
} 

Командная строка:

# icpc a1.cc a2.cc main.cc -o main
# ./main
0 != 0

Мой вопрос: это соответствует? Приводит ли использование статических локальных переменных к таким экземплярам к неопределенному поведению? При проверке созданных символов я заметил, что, хотя f имеет локальную связь, как я и подозревал, статическая переменная x получает слабую связь, и поэтому два x объединяются, и становится лотереей, которая выбрана

# icpc a2.cc a1.cc main.cc -o main
# ./main
1 != 1

Буду признателен за помощь. Возможно, это на самом деле ошибка компилятора, и о ней уже сообщалось?


person Johannes Schaub - litb    schedule 21.10.2015    source источник
comment
это также происходит, если вы переименовываете один из struct A, например. struct B?   -  person m.s.    schedule 21.10.2015
comment
Разве определение f не нарушает ODR? Что произойдет, если вы поместите два определения f в соответствующие им локальные пространства имен, чтобы исправить нарушение ODR?   -  person MSalters    schedule 21.10.2015
comment
@MSalters то же самое происходит с boost::function, которую я не могу так легко изменить :) Если это нарушение ODR, я думаю, это противоречит существующей практике. Я не нашел правила, запрещающего это. Связывание экземпляра шаблона функции является внешним, но тип аргумента имеет внутреннюю связь (и, следовательно, это разные типы в разных TU!). Я не могу найти параграф, запрещающий это, и я не уверен, что есть причина запрещать это.   -  person Johannes Schaub - litb    schedule 21.10.2015
comment
@JohannesSchaub-litb: Как вы пришли к двум определениям Boost.function? Макрос? ODR в основном говорит, что последовательности токенов должны быть идентичными, но, конечно, значение токенов может отличаться после создания экземпляра. В конце концов, в этом весь смысл шаблонов.   -  person MSalters    schedule 21.10.2015
comment
@MSalters Я просто создаю boost::function<int()> f(MyTuLocalFunctor());, и он случайным образом заменяет поведение одного функтора .cc поведением другого функтора .cc. И оказалось, что причина в том, что он объединяет статические локальные переменные, которые boost::function использует для внутренней реализации стирания своего типа.   -  person Johannes Schaub - litb    schedule 21.10.2015
comment
Если вы убедитесь, что экземпляры f<T> не встроены в f1 и f2 (возможно, с __attribute__((noinline))), вы сможете увидеть, имеют ли эти экземпляры одно и то же искажение имени или нет. Согласно Itanium C++ ABI, изменение имени области видимости функции включает в себя изменение имени функции, и я определенно ожидаю, что два экземпляра будут иметь разное изменение имени. Я подозреваю, что у icpc есть ошибка в наличии инлайнинга.   -  person John Calsbeek    schedule 21.10.2015
comment
@JohannesSchaub-litb: статические локальные переменные внутри функции (шаблона), вне любого анонимного пространства имен? Я могу себе представить, что искажение имен не дает четких имен.   -  person MSalters    schedule 21.10.2015
comment
@ 865719 Я думаю, что мой случай не такой, потому что, хотя шаблон один и тот же, создаются две разные функции, потому что в соответствии с эквивалентностью типов 14.4p1 два идентификатора шаблона относятся к одному и тому же классу или функции, если ... их соответствующий тип шаблонные аргументы одного типа. В моем случае эти два типа различаются (поскольку они имеют внутреннюю связь и определены в двух TU. Следовательно, согласно 14.8p2 каждая специализация шаблона функции, созданная из шаблона, имеет свою собственную копию любой статической переменной.   -  person Johannes Schaub - litb    schedule 21.10.2015
comment
@JohnCalsbeek: Похоже, что компилятор (вполне разумно) делает вывод, что такое искажение имен не нужно, потому что для его обнаружения требуется нарушение ODR.   -  person MSalters    schedule 21.10.2015
comment
@MSalters Изменение имени происходит внутри одной единицы перевода, и f<A#1> и f<A#2> потребуются разные изменения, даже если они были созданы из одного и того же шаблона. Даже если в этом примере есть нарушение ODR (что, вероятно, есть), я ожидаю, что искажение имен двух статических данных будет другим.   -  person John Calsbeek    schedule 21.10.2015
comment
@JohnCalsbeek мне кажется, что изменение объекта внутри безымянного пространства имен всегда одинаково (что неудивительно, потому что в противном случае компилятору пришлось бы придумывать уникальные имена каждый раз, когда он компилирует TU, содержащий такие объекты). Это само по себе не было бы проблемой, если бы сгенерированная функция и экземпляры, которые зависят от нее, не отображались бы в списке экспортируемых символов (а функции, которые выдает ICC, на самом деле имеют локальную связь... только не этот статический член данных...).   -  person Johannes Schaub - litb    schedule 21.10.2015
comment
Я думаю, что вы можете избежать нарушения ODR и по-прежнему создавать кейс сбоя, что означает, что, вероятно, есть ошибка. Просто заставьте инициализатор вашего статического x использовать константу или перечисление, определенные по-разному в каждой версии A. Слабая связь статики имеет смысл, потому что она должна быть объединена с другими копиями того же экземпляра, но это несовместимо с именем, которое не искажено, чтобы содержать идентификатор безымянного пространства имен.   -  person John Calsbeek    schedule 21.10.2015
comment
@JohnCalsbeek ах, теперь я вижу, в чем ошибка. Хорошо, это очевидно. поменяю и попробую еще раз   -  person Johannes Schaub - litb    schedule 21.10.2015
comment
@JohannesSchaub-litb Потому что у вас есть два определения f в глобальном пространстве имен с разной последовательностью токенов в их определениях? Я не думаю, что буква закона об ODR заботится о связи в этом случае.   -  person John Calsbeek    schedule 21.10.2015
comment
@JohnCalsbeek исправил, и действительно, он все еще не работает :) Спасибо.   -  person Johannes Schaub - litb    schedule 21.10.2015


Ответы (1)


Это похоже на ошибку для меня. Пусть A1 будет одним из экземпляров A, а A2 будет другим:

Я предполагаю, что статический x имеет слабую связь, поэтому компоновщик может объединять копии статического кода между несколькими копиями одного и того же экземпляра. (Например, если вам удалось создать экземпляр f<A1> в двух разных единицах перевода.)

Либо f<A1> и f<A2> должны иметь различное изменение имени, что приведет к тому, что две версии x будут иметь различное изменение имени (я думаю, что некоторые компиляторы фактически генерируют случайное значение, чтобы сделать имена в анонимных пространствах имен уникальными), либо x не должно иметь внутренней связи. (поскольку для создания экземпляра f использовался локальный тип, что должно сделать невозможным его репликацию в другой единице перевода).

person John Calsbeek    schedule 21.10.2015