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 std
za 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 std
przez 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::abs
po prostu „odnosi się” do ::abs
.
Pytanie, które pozostaje w tym przypadku, to dlaczego oprócz standardu ::abs
mogł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 ::abs
powoduje 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ę ::foo
z 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 std
i 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ć int
ze double
w 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 int
with double
również zmienimy się z abs
na 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.
abs
jest nieprawidłowa.