Специфікація мови дозволяє реалізаціям реалізовувати <cmath>шляхом декларування (та визначення) стандартних функцій у глобальному просторі імен, а потім перенесення їх у простір імен stdза допомогою декларацій use. Не визначено, чи застосовується такий підхід
20.5.1.2 Заголовки
4 [...] У стандартній бібліотеці С ++, однак, оголошення (за винятком імен, які визначені як макроси в С) знаходяться в межах простору імен (6.3.6) простору імен std. Не вказано, чи спочатку ці імена (включаючи будь-які перевантаження, додані в пунктах 21 - 33 та Додатку D) спочатку оголошуються в межах загального простору імен, а потім вводяться у простір імен stdза допомогою явних декларацій use (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 ++. Якщо вищевказане пояснення належним чином описує внутрішню механіку проблеми, то основна частина цього залежить від заміни слабких символів для версій функцій у стилі С.
Зверніть увагу, що якщо ми просто глобально замінимо intна doubleу наведеній вище програмі, код (згідно з GCC) буде поводитися "як очікувалося" - він виведе -5 5. Це трапляється, оскільки стандартна бібліотека C не має abs(double)функції. Декларуючи своє abs(double), ми нічого не замінюємо.
Але якщо після перемикання з за intдопомогою doubleми також перемикаємося з absна fabs, оригінальна дивна поведінка знову з’явиться у повній красі (вихід -5 -5).
Це узгоджується з наведеним вище поясненням.
absнеправильна.