#include <iostream>
#include <cmath>
/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
return a > 0? -a : a;
}
int main() {
int a = abs(-5);
int b = std::abs(-5);
std::cout<< a << std::endl << b << std::endl;
return 0;
}
Saya berharap bahwa outputnya adalah -5
dan 5
, tetapi outputnya adalah -5
dan -5
.
Saya heran kenapa kasus ini bisa terjadi?
Apakah ada hubungannya dengan penggunaan std
atau apa?
abs
salah.abs
efek rusak inistd::abs()
.5
dan5
dengan dentang,-5
dan-5
dengan gcc.return 0
- yang akan menghindari orang berpikir Anda tidak sengaja menerapkan fungsi secara tidak benar dan membuat perilaku yang diinginkan dan sebenarnya lebih jelas.Jawaban:
Spesifikasi bahasa memungkinkan implementasi untuk diimplementasikan
<cmath>
dengan mendeklarasikan (dan mendefinisikan) fungsi standar dalam namespace global dan kemudian membawanya ke namespacestd
dengan menggunakan deklarasi menggunakan. Tidak ditentukan apakah pendekatan ini digunakanRupanya, Anda berurusan dengan salah satu implementasi yang memutuskan untuk mengikuti pendekatan ini (misalnya GCC). Yaitu implementasi Anda menyediakan
::abs
, sementarastd::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 penggantianstd::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
File sumber lain
Dalam kasus ini, Anda juga akan mengamati bahwa definisi baru
::foo
("Goodbye!"
) di file sumber kedua juga memengaruhi perilakuN::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 tersebutstd
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
dengandouble
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 memilikiabs(double)
fungsi. Dengan menyatakan milik kita sendiriabs(double)
, kita tidak mengganti apapun.Tetapi jika setelah beralih dari
int
dengandouble
kami juga beralih dariabs
kefabs
, perilaku aneh yang asli akan muncul kembali dalam kemuliaan penuh (output-5 -5
).Hal ini sesuai dengan penjelasan di atas.
sumber
using ::abs;
suka untukusing ::asin;
sehingga Anda dapat menimpa deklarasi, hal lain yang perlu disebutkan adalah bahwa fungsi namespace yang ditentukan dalam std tidak dideklarasikan untuk int melainkan untuk ganda , float#include<cmath>
di kode saya, saya mendapat jawaban yang sama.`abs
dapat dideklarasikan<cstdlib>
juga, yang mungkin secara implisit disertakan melalui<iostream>
. Coba hapus milik Andaabs
dan lihat apakah masih terkompilasi.Kode Anda menyebabkan perilaku tidak terdefinisi.
C ++ 17 [extern.names] / 4:
Jadi Anda tidak bisa membuat fungsi dengan prototipe yang sama dengan fungsi pustaka C Standar
int abs(int);
. Terlepas dari header mana yang sebenarnya Anda sertakan atau apakah header tersebut juga menempatkan nama library C ke dalam namespace global.Namun, akan diizinkan untuk membebani
abs
jika Anda memberikan jenis parameter yang berbeda.sumber