Спецификация языка позволяет реализовать реализации <cmath>
, объявляя (и определяя) стандартные функции в глобальном пространстве имен, а затем помещая их в пространство имен std
с помощью объявлений-использования. Не указано, используется ли этот подход.
20.5.1.2 Заголовки
4 [...] Однако в стандартной библиотеке C ++ объявления (за исключением имен, которые определены как макросы в C) находятся в области видимости пространства имен (6.3.6) пространства имен std
. Не указано, были ли эти имена (включая любые перегрузки, добавленные в разделах 21–33 и приложении D) сначала объявлены в области глобального пространства имен, а затем введены в пространство имен std
явными объявлениями using (10.3.3).
По-видимому, вы имеете дело с одной из реализаций, которые решили следовать этому подходу (например, GCC). Т.е. ваша реализация предусматривает ::abs
, а std::abs
просто "ссылается" на ::abs
.
В этом случае остается один вопрос: почему в дополнение к стандарту ::abs
вы могли объявить свой собственный ::abs
, т.е. почему нет множественной ошибки определения. Это может быть вызвано другой функцией, предоставляемой некоторыми реализациями (например, GCC): они объявляют стандартные функции как так называемые слабые символы , что позволяет вам «заменить» их собственными определениями.
Эти два фактора вместе создают эффект, который вы наблюдаете: замена слабых символов ::abs
также приводит к замене std::abs
. Насколько это согласуется с языковым стандартом - это совсем другое дело ... В любом случае не полагайтесь на такое поведение - оно не гарантируется языком.
В GCC это поведение можно воспроизвести на следующем минималистичном примере. Один исходный файл
#include <iostream>
void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }
Другой исходный файл
#include <iostream>
void foo();
namespace N { using ::foo; }
void foo() { std::cout << "Goodbye!" << std::endl; }
int main()
{
foo();
N::foo();
}
В этом случае вы также заметите, что новое определение ::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
with double
мы также переключимся с abs
на fabs
, исходное странное поведение снова проявится во всей красе (результат -5 -5
).
Это согласуется с приведенным выше объяснением.
abs
неверна.