Specyfikacja języka umożliwia implementację implementacji <cmath>poprzez deklarowanie (i definiowanie) standardowych funkcji w globalnej przestrzeni nazw, a następnie przenoszenie ich do przestrzeni nazw stdza pomocą deklaracji using. Nie określono, czy takie podejście jest stosowane
20.5.1.2 Nagłówki
4 [...] Jednak w standardowej bibliotece C ++ deklaracje (z wyjątkiem nazw, które są zdefiniowane jako makra w C) mieszczą się w zakresie przestrzeni nazw (6.3.6) tej przestrzeni nazw std. Nie jest określone, czy te nazwy (w tym wszelkie przeciążenia dodane w klauzulach od 21 do 33 i załączniku D) są najpierw deklarowane w zakresie globalnej przestrzeni nazw, a następnie są wprowadzane do przestrzeni nazw stdprzez jawne deklaracje użycia (10.3.3).
Najwyraźniej masz do czynienia z jedną z implementacji, która zdecydowała się na takie podejście (np. GCC). Oznacza to, że Twoja implementacja zapewnia ::abs, a std::abspo prostu „odnosi się” do ::abs.
Pytanie, które pozostaje w tym przypadku, to dlaczego oprócz standardu ::absmogłeś zadeklarować swój własny ::abs, tj. Dlaczego nie ma błędu wielokrotnej definicji. Może to być spowodowane inną cechą udostępnianą przez niektóre implementacje (np. GCC): deklarują one standardowe funkcje jako tzw. Słabe symbole , dzięki czemu można je „zastąpić” własnymi definicjami.
Te dwa czynniki razem tworzą efekt, który obserwujesz: słaba zamiana symbolu ::abspowoduje również zastąpienie std::abs. Jak dobrze to zgadza się ze standardem językowym, to inna historia… W każdym razie nie polegaj na tym zachowaniu - nie gwarantuje tego język.
W GCC to zachowanie można odtworzyć na następującym minimalistycznym przykładzie. Jeden plik źródłowy
#include <iostream>
void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }
Inny plik źródłowy
#include <iostream>
void foo();
namespace N { using ::foo; }
void foo() { std::cout << "Goodbye!" << std::endl; }
int main()
{
foo();
N::foo();
}
W takim przypadku zauważysz również, że nowa definicja ::foo( "Goodbye!") w drugim pliku źródłowym również wpływa na zachowanie N::foo. Zostaną wyświetlone oba wywołania "Goodbye!". A jeśli usuniesz definicję ::fooz drugiego pliku źródłowego, oba wywołania będą kierowane do „oryginalnej” definicji::foo i danych wyjściowych "Hello!".
Zezwolenie udzielone przez powyższy 20.5.1.2/4 ma na celu uproszczenie implementacji <cmath>. Implementacje mogą po prostu zawierać styl C <math.h>, a następnie ponownie zadeklarować funkcje stdi dodać niektóre C ++ - specyficzne dodatki i poprawki. Jeśli powyższe wyjaśnienie właściwie opisuje wewnętrzną mechanikę problemu, to większa jego część zależy od zastępowalności słabych symboli na wersji funkcji w stylu C.
Zauważ, że jeśli po prostu globalnie zastąpić intze doublew powyższym programie, kod (pod GCC) będą zachowywać się „jak oczekiwano” - to będzie wyjście -5 5. Dzieje się tak, ponieważ standardowa biblioteka C nie ma abs(double)funkcji. Deklarując własne abs(double), niczego nie zastępujemy.
Ale jeśli po przełączeniu z intwith doublerównież zmienimy się z absna fabs, oryginalne dziwne zachowanie pojawi się ponownie w pełnej okazałości (wyjście -5 -5).
Jest to zgodne z powyższym wyjaśnieniem.
absjest nieprawidłowa.