#include <iostream>
#include <cmath>
/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
return a > 0? -a : a;
}
int main() {
int a = abs(-5);
int b = std::abs(-5);
std::cout<< a << std::endl << b << std::endl;
return 0;
}
Я ожидал, что на выходе будет -5
и 5
, но на выходе будет -5
и -5
.
Интересно, почему это случится?
Это как-то связано с использованием std
или как?
abs
неверна.abs
аффектаstd::abs()
.5
и5
с clang,-5
и-5
с gcc.return 0
- это позволило бы избежать того, чтобы люди думали, что вы непреднамеренно реализовали функцию неправильно, и сделало бы желаемое и фактическое поведение более ясным.Ответы:
Спецификация языка позволяет реализовать реализации
<cmath>
, объявляя (и определяя) стандартные функции в глобальном пространстве имен, а затем помещая их в пространство именstd
с помощью объявлений-использования. Не указано, используется ли этот подход.По-видимому, вы имеете дело с одной из реализаций, которые решили следовать этому подходу (например, GCC). Т.е. ваша реализация предусматривает
::abs
, аstd::abs
просто "ссылается" на::abs
.В этом случае остается один вопрос: почему в дополнение к стандарту
::abs
вы могли объявить свой собственный::abs
, т.е. почему нет множественной ошибки определения. Это может быть вызвано другой функцией, предоставляемой некоторыми реализациями (например, GCC): они объявляют стандартные функции как так называемые слабые символы , что позволяет вам «заменить» их собственными определениями.Эти два фактора вместе создают эффект, который вы наблюдаете: замена слабых символов
::abs
также приводит к заменеstd::abs
. Насколько это согласуется с языковым стандартом - это совсем другое дело ... В любом случае не полагайтесь на такое поведение - оно не гарантируется языком.В GCC это поведение можно воспроизвести на следующем минималистичном примере. Один исходный файл
Другой исходный файл
В этом случае вы также заметите, что новое определение
::foo
("Goodbye!"
) во втором исходном файле также влияет на поведениеN::foo
. Оба вызова будут выведены"Goodbye!"
. И если вы удалите определение::foo
из второго исходного файла, оба вызова будут отправлены в «исходное» определение::foo
и будут выводиться"Hello!"
.Разрешение, данное выше 20.5.1.2/4, предназначено для упрощения реализации
<cmath>
. Реализациям разрешается просто включать C-стиль<math.h>
, затем повторно объявлять функцииstd
и добавлять некоторые специфические для C ++ дополнения и настройки. Если приведенное выше объяснение правильно описывает внутреннюю механику проблемы, то большая часть ее зависит от возможности замены слабых символов для версий функций в стиле C.Обратите внимание, что если мы просто глобально заменим
int
наdouble
в приведенной выше программе, код (в GCC) будет вести себя «как ожидалось» - он будет выводиться-5 5
. Это происходит потому, что стандартная библиотека C неabs(double)
работает. Заявляя о своемabs(double)
, мы ничего не заменяем.Но если после переключения с
int
withdouble
мы также переключимся сabs
наfabs
, исходное странное поведение снова проявится во всей красе (результат-5 -5
).Это согласуется с приведенным выше объяснением.
источник
using ::abs;
подобного,using ::asin;
поэтому вы можете переопределить объявление, еще один момент, который следует упомянуть, заключается в том, что функции, определенные в пространстве имен std, объявлены не для int, а для double , float#include<cmath>
в своем коде, я получил тот же ответ »abs
может быть объявлен в<cstdlib>
, что может быть неявно включено через<iostream>
. Попробуйте удалить свой собственныйabs
и посмотреть, компилируется ли он.Ваш код вызывает неопределенное поведение.
C ++ 17 [extern.names] / 4:
Таким образом , вы не можете создать функцию с тем же прототипом как функции стандартной библиотеки C
int abs(int);
. Независимо от того, какие заголовки вы фактически включаете, или же эти заголовки также помещают имена библиотек C в глобальное пространство имен.Однако возможна перегрузка,
abs
если вы укажете другие типы параметров.источник