Aturan emas C ++ "as-if" 1 menyatakan bahwa, jika perilaku program yang dapat diamati tidak bergantung pada keberadaan anggota data yang tidak digunakan, kompilator diizinkan untuk mengoptimalkannya .
Apakah variabel anggota yang tidak digunakan menggunakan memori?
Tidak (jika "benar-benar" tidak digunakan).
Sekarang muncul dua pertanyaan dalam benak:
- Kapan perilaku yang dapat diamati tidak bergantung pada keberadaan anggota?
- Apakah situasi seperti itu terjadi dalam program kehidupan nyata?
Mari kita mulai dengan sebuah contoh.
Contoh
#include <iostream>
struct Foo1
{ int var1 = 5; Foo1() { std::cout << var1; } };
struct Foo2
{ int var1 = 5; int var2; Foo2() { std::cout << var1; } };
void f1() { (void) Foo1{}; }
void f2() { (void) Foo2{}; }
Jika kita meminta gcc untuk mengkompilasi unit terjemahan ini , ia akan menampilkan:
f1():
mov esi, 5
mov edi, OFFSET FLAT:_ZSt4cout
jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
f2():
jmp f1()
f2
adalah sama dengan f1
, dan tidak ada memori yang pernah digunakan untuk menyimpan yang sebenarnya Foo2::var2
. ( Dentang melakukan hal serupa ).
Diskusi
Beberapa orang mungkin mengatakan ini berbeda karena dua alasan:
- ini contoh yang terlalu sepele,
- struct sepenuhnya dioptimalkan, itu tidak dihitung.
Nah, program yang bagus adalah kumpulan hal-hal sederhana yang cerdas dan kompleks, bukan penjajaran sederhana dari hal-hal kompleks. Dalam kehidupan nyata, Anda menulis banyak sekali fungsi sederhana menggunakan struktur sederhana daripada yang dioptimalkan oleh compiler. Contohnya:
bool insert(std::set<int>& set, int value)
{
return set.insert(value).second;
}
Ini adalah contoh asli dari anggota data (di sini, std::pair<std::set<int>::iterator, bool>::first
) yang tidak digunakan. Tebak apa? Itu dioptimalkan jauh ( contoh sederhana dengan set boneka jika perakitan itu membuat Anda menangis).
Sekarang akan menjadi waktu yang tepat untuk membaca jawaban yang sangat baik dari Max Langhof (tolong beri saya suara positif ). Ini menjelaskan mengapa, pada akhirnya, konsep struktur tidak masuk akal pada tingkat perakitan keluaran kompilator.
"Tapi, jika saya melakukan X, fakta bahwa anggota yang tidak terpakai dioptimalkan adalah masalah!"
Ada sejumlah komentar yang menyatakan jawaban ini pasti salah karena beberapa operasi (seperti assert(sizeof(Foo2) == 2*sizeof(int))
) akan merusak sesuatu.
Jika X adalah bagian dari perilaku program 2 yang dapat diamati , kompilator tidak diizinkan untuk mengoptimalkan semuanya. Ada banyak operasi pada objek yang berisi anggota data "tidak terpakai" yang akan memiliki efek yang dapat diamati pada program. Jika operasi seperti itu dilakukan atau jika kompilator tidak dapat membuktikan tidak ada yang dijalankan, anggota data yang "tidak terpakai" adalah bagian dari perilaku program yang dapat diamati dan tidak dapat dioptimalkan .
Operasi yang mempengaruhi perilaku yang dapat diamati termasuk, tetapi tidak terbatas pada:
- mengambil ukuran dari sebuah tipe object (
sizeof(Foo)
),
- mengambil alamat anggota data yang dideklarasikan setelah yang "tidak digunakan",
- menyalin objek dengan fungsi seperti
memcpy
,
- memanipulasi representasi objek (seperti dengan
memcmp
),
- memenuhi syarat suatu objek sebagai volatile ,
- dll .
1)
[intro.abstract]/1
Deskripsi semantik dalam dokumen ini mendefinisikan mesin abstrak nondeterministik berparameter. Dokumen ini tidak menempatkan persyaratan pada struktur implementasi yang sesuai. Secara khusus, mereka tidak perlu menyalin atau meniru struktur mesin abstrak. Sebaliknya, implementasi yang sesuai diperlukan untuk meniru (hanya) perilaku yang dapat diamati dari mesin abstrak seperti yang dijelaskan di bawah ini.
2) Seperti menegaskan lulus atau gagal.