Почему в Itanium C++ ABI искаженное имя для функций шаблона не разрешает зависимые определения типов?

Например:

template <typename T>
struct foo
{
    using bar = int;
};

// _Z3bazi
void baz(foo<int>::bar quux) {
}

template <typename T>
void baz(typename foo<T>::bar quux) {
}

// _Z3bazIiEvN3fooIT_E3barE
template void baz<int>(foo<int>::bar quux);

Почему в искаженной форме baz<int> вообще упоминается foo? Почему это не _Z3bazIiEvi?

Это, по-видимому, причина того, что предложение C++17 std::default_order<T> мертво в воде.


person Tavian Barnes    schedule 20.10.2016    source источник
comment
Чтобы в разобранном виде он выглядел как исходный код?   -  person BoBTFish    schedule 20.10.2016
comment
соответствующее обсуждение на Reddit   -  person krzaq    schedule 20.10.2016
comment
Я не пометил [gcc], потому что как вопрос ABI он также относится к Clang и т. д. Я воспроизвел проблему с Clang, и это очень удивительно… невероятно, что это может соответствовать стандарту C++. Итак, первый шаг — проверить, что abi действительно предписывает Это.   -  person Potatoswatter    schedule 20.10.2016
comment
@Potatoswatter Действительно, Itanium ABI не относится к gcc. Но я все равно отметил [gcc], потому что чувствовал, что эксперты gcc могут обладать необходимыми знаниями, чтобы ответить на этот вопрос. Кроме того, я считаю, что многие правила искажения были изобретены gcc и кодифицированы в Itanium ABI позже, не так ли?   -  person Tavian Barnes    schedule 20.10.2016
comment
@Barry Несоответствие возникает, если вы можете заметить, что существует отдельный символ, например, если два имени одной специализации дают разные адреса. Сначала я думал, что Clang позволяет мне сгенерировать одну специализацию дважды, но на самом деле это не так.   -  person Potatoswatter    schedule 20.10.2016


Ответы (1)


Проблема связана с конструкцией <unresolved-name> в ABI. Зачем нам когда-либо хотеть использовать неразрешенное имя? Все дело в сопоставлении объявлений и перегрузках. С++ 14 §14.5.6.1/3 примечания,

Два разных шаблона функций могут иметь идентичные типы возвращаемых функций и списки параметров функций, даже если разрешение перегрузки не может их различить.

У вас может быть другая функция в другом файле,

template <typename T>
void baz(int quux) { std::abort(); }

Хотя эта сигнатура не может мирно сосуществовать в одном и том же файле — она не может быть названа из-за двусмысленности перегрузки — она может существовать в другом файле, поэтому ее необходимо изменить.

(Даже такой уровень сосуществования не гарантируется стандартом для всех шаблонов. Вопрос QOI заключается в том, что компилятор использует точную форму объявления шаблона функции для выполнения сопоставления объявлений, так что копирование и вставка объявления в определение будет иметь тенденцию чтобы обеспечить точное совпадение, а не неожиданный конфликт с другим шаблоном функции, который разрешается в ту же сигнатуру (см. §14.5.6.1/5-6).

Что касается дождя на параде default_order, проблема заключается в том, что идентификаторы шаблонов неявно извлекают аргументы по умолчанию из шаблонов. Таким образом, пользователь может непреднамеренно иметь зависимое имя типа в подписи, просто упомянув std::set.

person Potatoswatter    schedule 20.10.2016
comment
Спасибо! Вы уверены, что template <typenamte T> void baz(int) действительно разрешено в отдельной единице перевода? Я всегда предполагал, что это одна из тех уродливых вещей, не требующих диагностики. В частности, судя по всему, MSVC коверкает оба одинаково. Это действительно несовместимо? - person Tavian Barnes; 20.10.2016
comment
@TavianBarnes NDR возможен в соответствии с §14.5.6.1/6, если они функционально эквивалентны, но не эквивалентны. Немного странно, что правило указывает сравнивать объявления шаблонов в соответствии с процессом, определенным только для выражений, поэтому оно говорит о значениях, когда у нас есть только типы. Я отредактирую… - person Potatoswatter; 20.10.2016
comment
функционально эквивалентный означает, что для любого заданного набора аргументов шаблона вычисление выражения приводит к одному и тому же значению. Даже если бы подобное правило применялось к типам, оно не применялось бы к default_order, который предназначен для возврата чего-то другого. - person T.C.; 21.10.2016
comment
@Т.С. Эквивалентность — это когда выражение (или зависимое имя типа) выражается аналогичным образом по модулю идентификаторов параметров. Функциональная эквивалентность — это когда оценка с учетом всех фактических аргументов шаблона приводит к одинаковым значениям (или зависимым типам?). Для примера default_order функциональная эквивалентность имеет смысл только в контексте использования после любой настройки. Эквивалентность определяется при обработке деклараций; функциональная эквивалентность может быть определена при разрешении перегрузки. Оба всегда применимы, хотя они актуальны на разных этапах компиляции. - person Potatoswatter; 21.10.2016
comment
Вы, кажется, читаете для любого, поскольку существует любой. Я читаю это как для каждого, так что два выражения функционально эквивалентны только в том случае, если не существует набора аргументов, для которых два выражения будут давать разные значения. Обратите внимание, что функциональная эквивалентность — это свойство двух шаблонов функций, а не двух специализаций шаблонов функций, поэтому вы, кажется, говорите, что template<int I, int J> void f(A<I * J>); template<int I, int J> void f(A<I + J>); — это неправильно сформированный отчет о недоставке, что для меня не имеет никакого смысла. - person T.C.; 21.10.2016
comment
@Т.С. Вы правильно читаете, но хотя это свойство шаблонов (а не их специализаций), функциональная эквивалентность сигнатуры шаблона функции использование default_order может все же зависеть от специализаций default_order. Ваш пример, безусловно, является плохо сформированным отчетом о недоставке. Подключите I=0, J=0 и у вас есть заданный набор аргументов шаблона, оценка выражения приводит к тому же значению. Любая возникшая путаница является оправданием для чувствительности Itanium ABI к QOI. - person Potatoswatter; 21.10.2016
comment
@Potatoswatter Нет, I + J и I * J в моем понимании функционально не эквивалентны, потому что существуют некоторые наборы аргументов, для которых они не имеют одинакового значения (например, I=1, J=1). Вы читаете любое как ∃, я читаю как ∀. Действительно, при вашем прочтении даже пример двумя абзацами выше в [temp.over.link]/4 был бы неправильным отчетом о недоставке. Если бы это было намерением, я не могу представить, почему они не назвали бы это там. - person T.C.; 21.10.2016
comment
@Т.С. О, теперь я понимаю, что вы имеете в виду. ∀, вероятно, правильное прочтение, но, по-видимому, не то, что делает MSVC, поэтому оно может быть несоответствующим. Я хеджировал этот ответ тем, что может не быть гарантированным. Однако это ортогонально вопросу о default_order специализациях. Диагностика отчета о недоставке применяется очень лениво во время компоновки, если вообще применяется, в то время как во время объявления шаблона вы можете мыслить более активно. - person Potatoswatter; 21.10.2016
comment
Но они связаны; если для каждого T, default_order_t<T> и std::less<T> одного и того же типа - а они не являются - тогда template<class T> void f(default_order_t<T>); и template<class T> void f(std::less<T>); являются отдельными шаблонами функций, которые не нарушают никаких правил в стандарте, и компилятор должен убедиться, что их специализации не соединены ошибочно. - person T.C.; 21.10.2016
comment
@Т.С. Itanium ABI, похоже, работает с ∀, что сводит к минимуму неинтуитивное связывание (но также не позволяет связывать вместе, например, A+B с B+A). MSVC, кажется, идет с ∃ (или у него просто есть ошибка ABI), и перегрузочно-неоднозначные подписи могут конфликтовать в компоновщике. Ошибки в любом случае возникают естественным образом из-за компоновщика, а не из-за попытки фактически доказать эквивалентность. Например, MSVC обычно может давать одинаковые имена специализациям двух ваших шаблонов f, за исключением случаев, когда default_order является специализированным, тогда имена будут разными и ошибочной привязки не будет. - person Potatoswatter; 21.10.2016
comment
@Т.С. … Если default_order не специализирована, то MSVC делает связь между этими двумя формами (предположим, что я прав; я не могу проверить, потому что у меня нет копии) . Это, вероятно, то, что вы действительно получаете. Но при их прочтении ∃ это будет неправильно сформированный NDR, поэтому связь, даже если она ошибочна, на самом деле в порядке. - person Potatoswatter; 21.10.2016
comment
Вопрос QOI заключается в том, что компилятор использует точную форму объявления шаблона функции для выполнения сопоставления объявлений, так что копирование и вставка объявления в определение, как правило, обеспечивает точное совпадение, а не неожиданный конфликт с другим шаблоном функции, который разрешает к той же подписи. - поскольку foo можно специализировать так, чтобы он давал не-int типы, я с вами не согласен. Шаблоны функционально не эквивалентны AFAICS. - person Johannes Schaub - litb; 14.05.2017
comment
(обратите внимание, что в настоящее время я думаю, что абзацы, которые вы цитируете, применяются только к спискам параметров шаблона или возвращаемым типам, которые относятся к выражениям: два шаблона функций функционально эквивалентны, если они эквивалентны, за исключением того, что одно или несколько выражений включают шаблон параметры в возвращаемых типах и списках параметров функционально эквивалентны с использованием описанных выше правил для сравнения выражений, включающих параметры шаблона. - person Johannes Schaub - litb; 14.05.2017
comment
Насколько я знаю, это предназначено также для охвата выражений типа - в смысле и предпринимается попытка найти значения аргумента шаблона (тип для параметра типа,... из [temp.deduct], который немного менее формален в его использование значений, точно так же, как ваши ссылочные абзацы немного менее формальны Но, может быть, я что-то упускаю из виду. - person Johannes Schaub - litb; 14.05.2017
comment
@JohannesSchaub-litb 1. В предыдущем предложении я усилил слово may not. Абзац в скобках не предназначен для применения к рассматриваемому примеру; да, это включает в себя выражения. 2. Даже если бы компоновщик видел только полностью созданные шаблоны, у него не было бы проблем со специализированными foo. Программе разрешено иметь только одну спецификацию члена для каждой специализации. - person Potatoswatter; 15.05.2017
comment
@JohannesSchaub-litb В стандарте упоминаются возвращаемые типы и списки параметров, которые эквивалентны с использованием описанных выше правил для сравнения выражений, включающих параметры шаблона — правила эквивалентности используются для учета различий идентификаторов, когда выражения не используются, но я не вижу как (просто) функциональная эквивалентность может иметь место без выражения, как, например, в данном примере. - person Potatoswatter; 15.05.2017