Разные функции имеют разные адреса?

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

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 складывание легально)

4 ответа

Решение

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

1 == (равно) и тому != (не равно) группа операторов слева направо. Операнды должны иметь арифметику, перечисление, указатель или указатель на тип или тип члена 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: Равенство указателей на функции решает эту проблему и мне кажется, что это нормально, но, как показывают комментарии, есть разногласия. Это говорит (акцент мой):

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

и ответ был:

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

Вопрос задает два вопроса:

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

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

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

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

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

Поскольку мы знаем, что имеем дело с правилом " как будто", мы можем начать с этого 1.8 говорит:

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

и обратите внимание 4 говорит:

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

но записка из этого раздела гласит:

Функция не является объектом, независимо от того, занимает ли она память так, как это делают объекты

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

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

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

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

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

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

наблюдения

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

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

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

а также:

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

Как я отмечал в своем комментарии, стандарт C требует, чтобы эти макросы генерировали различные значения из 7.14 в С11:

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

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

Обновить

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам