Spesifikasi bahasa memungkinkan implementasi untuk diimplementasikan <cmath>
dengan mendeklarasikan (dan mendefinisikan) fungsi standar dalam namespace global dan kemudian membawanya ke namespace std
dengan menggunakan deklarasi menggunakan. Tidak ditentukan apakah pendekatan ini digunakan
20.5.1.2 Headers
4 [...] Namun, di pustaka standar C ++, deklarasi (kecuali untuk nama yang didefinisikan sebagai makro di C) berada dalam cakupan namespace (6.3.6) dari namespace std
. Tidak ditentukan apakah nama-nama ini (termasuk kelebihan beban yang ditambahkan dalam Klausul 21 hingga 33 dan Lampiran D) pertama kali dideklarasikan dalam cakupan namespace global dan kemudian dimasukkan ke dalam namespace std
dengan menggunakan deklarasi penggunaan eksplisit (10.3.3).
Rupanya, Anda berurusan dengan salah satu implementasi yang memutuskan untuk mengikuti pendekatan ini (misalnya GCC). Yaitu implementasi Anda menyediakan ::abs
, sementara std::abs
hanya "mengacu" ke ::abs
.
Satu pertanyaan yang tersisa dalam kasus ini adalah mengapa selain standar ::abs
Anda dapat mendeklarasikan milik Anda sendiri ::abs
, yaitu mengapa tidak ada kesalahan definisi ganda. Hal ini mungkin disebabkan oleh fitur lain yang disediakan oleh beberapa implementasi (mis. GCC): mereka mendeklarasikan fungsi standar yang disebut simbol lemah , sehingga memungkinkan Anda untuk "menggantinya" dengan definisi Anda sendiri.
Kedua faktor ini bersama-sama menciptakan efek yang Anda amati: penggantian simbol lemah ::abs
juga menghasilkan penggantian std::abs
. Seberapa baik hal ini sesuai dengan standar bahasa adalah cerita yang berbeda ... Bagaimanapun, jangan mengandalkan perilaku ini - ini tidak dijamin oleh bahasa.
Di GCC, perilaku ini dapat direproduksi dengan contoh minimalis berikut. Satu file sumber
#include <iostream>
void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }
File sumber lain
#include <iostream>
void foo();
namespace N { using ::foo; }
void foo() { std::cout << "Goodbye!" << std::endl; }
int main()
{
foo();
N::foo();
}
Dalam kasus ini, Anda juga akan mengamati bahwa definisi baru ::foo
( "Goodbye!"
) di file sumber kedua juga memengaruhi perilaku N::foo
. Kedua panggilan akan keluar "Goodbye!"
. Dan jika Anda menghapus definisi dari ::foo
file sumber kedua, kedua panggilan akan dikirim ke definisi "asli" ::foo
dan keluaran "Hello!"
.
Izin yang diberikan oleh 20.5.1.2/4 di atas ada untuk menyederhanakan implementasi <cmath>
. Implementasi diperbolehkan untuk hanya memasukkan C-style <math.h>
, kemudian mendeklarasikan kembali fungsi-fungsi tersebut std
dan menambahkan beberapa tambahan dan tweak khusus C ++. Jika penjelasan di atas dengan tepat mendeskripsikan mekanisme bagian dalam dari masalah tersebut, maka sebagian besar bergantung pada kemampuan penggantian simbol yang lemah untuk versi C-style dari fungsi tersebut.
Perhatikan bahwa jika kita hanya mengganti secara global int
dengan double
dalam program di atas, kode (di bawah GCC) akan berperilaku "seperti yang diharapkan" - itu akan keluar -5 5
. Ini terjadi karena pustaka standar C tidak memiliki abs(double)
fungsi. Dengan menyatakan milik kita sendiri abs(double)
, kita tidak mengganti apapun.
Tetapi jika setelah beralih dari int
dengan double
kami juga beralih dari abs
ke fabs
, perilaku aneh yang asli akan muncul kembali dalam kemuliaan penuh (output -5 -5
).
Hal ini sesuai dengan penjelasan di atas.
abs
salah.