Имеют ли отдельные функции разные адреса?



рассмотрим эти две функции:

void foo() {}
void bar() {}

гарантируется ли, что &foo != &bar?

аналогично,

template<class T> void foo() { }

гарантируется ли, что &foo<int> != &foo<double>?


есть два компоновщика, которые я знаю о том, что определения функции сгиба вместе.

MSVC агрессивно COMDAT складывает функции, поэтому две функции с одинаковой реализацией могут быть превращены в одну функцию. В качестве побочного эффекта обе функции имеют один и тот же адрес. Я был под впечатлением, что это было незаконно, но я не могу найти, где в стандарте это сделано незаконно.

Золотой линкер также складывает функции, с обоими safe и all настройка. safe означает, что если адрес функции взят, то он не складывается, в то время как all складывается, даже если адрес взят. Так что золото в складку safe ведет себя как-если функции имеют разные адреса.

в то время как складывание может быть неожиданным, и есть код это зависит от различных (идентичных реализаций) функций, имеющих разные адреса (поэтому может быть опасно сворачивать), действительно ли это незаконно в соответствии с текущим стандартом C++? (C++14 в этот момент) (естественно, как-если safe складывание является законным)

27   4  

4 ответов:

5.10 операторы равенства [expr.eq]

1 The == (равно) и != (не равно) группа операторов слева направо. Операнды должны иметь арифметику, перечисление, указатель или указатель на тип члена, или тип std::nullptr_t. Операторы == и != урожайность true или false, т. е. результат типа bool. В каждом случае ниже, операнды должен иметь тот же тип после указанных преобразований были применяется.
2 если хотя бы один из операндов является указателем, преобразования указателя (4.10) и преобразования квалификации (4.4) выполняются на обоих операндах, чтобы привести их к их составному типу указателя (п. 5). Сравнение указателей определяется следующим образом: два указателя равны, если они оба нулевые, оба указывают на одну и ту же функцию, или оба представляют один и тот же адрес (3.9.2), в противном случае они сравнивают неравный.

давайте возьмем последний бит за бит:

  1. два нулевых указателя сравниваются равными.
    Хорошо для вашего здравомыслия.
  2. два указателя на одну и ту же функцию равны.
    Все остальное было бы крайне удивительно.
    Это также означает, что только одна из-за линии версия любого inline-функция может когда-либо иметь свой адрес, если вы не хотите сделать сравнение функций-указателей чрезмерно сложным и дорогой.
  3. оба представляют один и тот же адрес.
    Теперь, когда одна, что все это значит. Отбрасывая это и уменьшая if and only if простой if оставил бы это для интерпретации, но это четкий мандат, чтобы сделать любые две функции идентичными, до тех пор, пока он не изменяет наблюдаемое поведение конформной программы.

похоже отчет о дефекте 1400: равенство указателя функции занимается этим вопросом и, как мне кажется, сказать, что это нормально, чтобы сделать эту оптимизацию, но как показывают комментарии, есть разногласия. Он говорит (выделено мной):

согласно 5.10 [expr.eq] пункт 2,только два указателя функций сравнить равными, если они указывают на одну и ту же функцию. Однако, как оптимизация, реализации в настоящее время функции сглаживания что имеют одинаковые определения. Неясно, нужен ли стандарт разбираться явно с этой оптимизацией или нет.

и ответ был:

стандарт ясен на требованиях, и реализации свободно оптимизировать в рамках ограничений правила "как-если".

вопрос задается о двух вопросах:

  • Is это нормально, что эти указатели считаются равными
  • можно ли объединить функции

на основе комментариев я вижу две интерпретации ответа:

  1. эта оптимизация в порядке, стандарт дает реализацию этой свободы под как-если правило. Элемент как-если правило рассматривается в разделе 1.9 и означает, что реализация должна только эмулировать наблюдаемое поведение относительно требования стандарта. это все еще моя интерпретация ответа.

  2. проблема под рукой полностью игнорируется, и в заявлении просто говорится, что никакая корректировка стандарта не требуется, потому что явно как-если норм охватывает это, но интерпретация оставлена в качестве упражнения для читателя. Хотя я признаю, что из-за краткости ответа я не могу отвергнуть эту точку зрения, она в конечном итоге является полностью бесполезный ответ. Это также кажется несовместимым с ответами в другом NAD вопросы, которые, насколько я могу сказать, указывают на проблему, если они существуют.

что говорится в проекте стандарта

так как мы знаем, что имеем дело с как-если правило, мы можем начать там и отметить, что раздел 1.8 говорит:

если объект не является битовым полем или базовым классом подобъекта нуля размер, адрес этот объект является адрес первого байта занимает. Два объекта, которые не являются битовыми полями, могут иметь одно и то же адрес, если один из них является подобъектом другого, или если хотя бы один из них является базовый класс подобъект нулевого размера и они бывают разных типов; в противном случае, они должны иметь различные адреса.4

и 4 говорит:

в соответствии с правилом "как-если" реализация может хранить два объекты на одной машине адрес или не хранить объект вообще, если программа не может наблюдать разницу

но в примечании из этого раздела говорится:

функция не является объектом, независимо от того, занимает она или нет хранение таким образом, что объекты делают

хотя это и не является нормативным, требования к объекту изложены в пункте 1 не имеет смысла в контексте функции, и поэтому она согласуется с этой запиской. Так что мы явно ограничены от объектов сглаживания с некоторыми исключениями, но не такое ограничение применяется к функциям.

Далее у нас есть раздел 5.10операторы равенства где сказано (выделено мной):

[...]Два указателя равны, если они оба null, оба пункта одна и та же функция или оба представляют один и тот же адрес (3.9.2), в противном случае они считаются равными.

что говорит нам, что два указателя равны, если они:

  • нулевые указатели
  • укажите на ту же функцию
  • представляют тот же адрес

The или оба представляют один и тот же адрес кажется, дает достаточно свободы, чтобы позволить компилятору псевдоним двух разных функций и не требует указателей на разные функции сравнения неравные.

замечания

Кит Томпсон сделал несколько замечательных наблюдений, которые, по моему мнению, стоит добавить к ответу, поскольку они касаются основных проблем, он говорит:

если программа выводит результат &foo = = & bar, это наблюдаемое поведение; рассматриваемая оптимизация изменяет наблюдаемое поведение.

С которым я согласен, и если бы мы могли показать, что существует требование, чтобы указатели были неравными, что действительно нарушило бы как-если правило но пока мы не можем показать, что.

и:

[...]рассмотрим программу, которая определяет пустые функции и использует их адреса как уникальные значения (подумайте о значение sig_dfl, SIG_ERR и SIG_IGN в /). Присвоение им одного и того же адреса будет сломайте такую программу

как я отметил в своем комментарии стандарт C не требует эти макросы для создания различных значений С 7.14 в C11:

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

поэтому, хотя этот случай рассматривается, возможно, есть и другие случаи, которые сделают эту оптимизацию опасный.

обновление

Ян Губичка а gcc разработчик написал в блог время связи и улучшения межпроцедурной оптимизации в GCC 5, сворачивание кода было одной из многих тем, которые он освещал.

Я попросил его прокомментировать, является ли сворачивание идентичных функций по одному и тому же адресу соответствующим поведением или нет, и он говорит, что это не соответствует поведению, и действительно такая оптимизация нарушит gcc сам по себе:

он не соответствует повороту двух функций, чтобы иметь один и тот же адрес, поэтому MSVC здесь довольно агрессивен. Это, например, нарушает сам GCC, потому что, к моему удивлению, сравнение адресов выполняется в предварительно скомпилированном коде заголовков. Он работает для многих других проектов, в том числе Firefox.

оглядываясь назад, после нескольких месяцев чтения отчетов о дефектах и размышлений о проблемах оптимизации, я склонен к более консервативному чтению ответ комитета. Взятие адреса функции является наблюдаемым поведением, и поэтому сворачивание идентичных функций нарушило бы как-если правило.

да. Из стандарта (§5.10 / 1): "два указателя одного и того же тип compare equal тогда и только тогда, когда они оба равны нулю, обе точки к одной и той же функции, или оба представляют один и тот же адрес"

Как только они были созданы, foo<int> и foo<double> это две разные функции, поэтому вышесказанное относится и к ним.

Так что проблемная часть явно фраза или оба представляют один и тот же адрес (3.9.2).

IMO эта часть явно предназначена для определения семантики типов указателей объектов. И только для типов указателей объектов.

фраза ссылается на раздел 3.9.2, что означает, что мы должны посмотреть там. 3.9.2 говорит (среди прочего) об адресах, которые представляют указатели объектов. Он не говорит об адресах, которые представляют указатели функций. Который, ИМО, оставляет только две возможные интерпретации:

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

2) фраза не применяется. Поскольку это относится к 3.9.2, который ничего не говорит об адресах, которые представляют указатели функций, мы можем сделать любой два указателя функций сравниваются равными. Что очень неожиданно и, конечно же, делает сравнение указателей функций совершенно бесполезным.

Итак, хотя технически можно сделать аргумент, что (2) является действительный интерпретация, ИМО это не значимую интерпретация и, таким образом, следует игнорировать. И поскольку не все, похоже, согласны с этим, я также думаю, что в стандарте требуется разъяснение.

    Ничего не найдено.

Добавить ответ:
Отменить.