Apakah mungkin untuk kode C ++ sesuai dengan standar C ++ 03 dan standar C ++ 11 , tetapi melakukan hal yang berbeda tergantung pada standar mana yang sedang dikompilasi?
c++
c++11
language-lawyer
c++03
Erik Sjölund
sumber
sumber
auto
dapat menghasilkan situasi seperti ini>>
ketika digunakan dalam templat. Anda dapat menemukan situasi di mana ia dapat dikompilasi untuk kedua standar. Satu lagi yang saya yakin akan mudah untuk menemukan perubahan adalah dalam inisialisasi.auto
bisa menyebabkan ini. Dengan makna lama,auto
deklarasi membutuhkan nama jenis; dengan makna baru, nama tipe tidak diizinkan.Jawaban:
Jawabannya pasti ya. Di sisi positifnya ada:
Di sisi negatif, beberapa contoh tercantum dalam lampiran C standar. Meskipun ada lebih banyak yang negatif daripada positif, masing-masing dari mereka jauh lebih kecil kemungkinannya untuk terjadi.
Literal string
dan
Ketik konversi 0
Dalam C ++ 11, hanya literal yang merupakan konstanta penunjuk integer nol:
Hasil bulat setelah pembagian bilangan bulat dan modulo
Dalam C ++ 03 kompiler diizinkan untuk membulatkan ke 0 atau menuju infinity negatif. Dalam C ++ 11 adalah wajib untuk membulatkan ke 0
Spasi putih antara kurung penutup templat bersarang >> vs>>
Di dalam spesialisasi atau instantiasi yang
>>
mungkin ditafsirkan sebagai pergeseran kanan dalam C ++ 03. Ini lebih mungkin untuk memecahkan kode yang ada: (dari http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )Operator
new
sekarang dapat melempar pengecualian lain selainstd::bad_alloc
Destruktor yang dideklarasikan oleh pengguna memiliki contoh spesifikasi pengecualian implisit dari Perubahan apa yang diperkenalkan di C ++ 11?
size()
kontainer sekarang diperlukan untuk berjalan di O (1)std::ios_base::failure
tidak berasal langsung daristd::exception
lagiSementara kelas dasar langsung adalah baru,
std::runtime_error
tidak. Jadi:sumber
noexecpt(true)
sehinggathrow
dalam destruktor sekarang akan memanggilstd::terminate
. Tapi saya harap siapa pun yang menulis kode seperti itu akan senang dengan ini!catch (std::exception &)
masih menangkapstd::ios_base::failure
.operator new
akurat (sekarang bisa melemparstd::bad_array_new_length
), tetapi contoh Anda tidak menunjukkan itu sama sekali. Kode yang Anda tampilkan adalah sama dalam C ++ 03 dan C ++ 11 AFAIK.Saya mengarahkan Anda ke artikel ini dan tindak lanjutnya , yang memiliki contoh yang bagus tentang bagaimana
>>
dapat mengubah makna dari C ++ 03 ke C ++ 11 sambil tetap mengkompilasi keduanya.Bagian kuncinya adalah baris dalam
main
, yang merupakan ekspresi.Di C ++ 03:
Dalam C ++ 11
Selamat, dua hasil berbeda untuk ekspresi yang sama. Memang, C + + 03 yang muncul dengan bentuk peringatan Dentang ketika saya mengujinya.
sumber
typename
untuk::two
di C ++ 03 versitrue
ataufalse
untuk standar yang berbeda. Mungkin kita bisa menggunakannya sebagai uji fitur </joke>warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]
), tetapi masih merupakan contoh yang bagus tentang bagaimana::
operator yang ambigu mengubah makna (baik merujuk ke lingkup global atau mereferensikan yang berdiri langsung sebelum itu)Ya, ada sejumlah perubahan yang akan menyebabkan kode yang sama menghasilkan perilaku yang berbeda antara C ++ 03 dan C ++ 11. Perbedaan aturan sekuensing membuat beberapa perubahan menarik termasuk beberapa perilaku yang sebelumnya tidak terdefinisi menjadi didefinisikan dengan baik.
1. beberapa mutasi dari variabel yang sama dalam daftar penginisialisasi
Satu kasus sudut yang sangat menarik adalah banyak mutasi dari variabel yang sama dalam daftar penginisialisasi, misalnya:
Baik dalam C ++ 03 dan C ++ 11 ini didefinisikan dengan baik tetapi urutan evaluasi dalam C ++ 03 tidak ditentukan tetapi dalam C ++ 11 mereka dievaluasi dalam urutan di mana mereka muncul . Jadi jika kita mengkompilasi menggunakan
clang
dalam mode C ++ 03 itu memberikan peringatan berikut ( lihat langsung ):tetapi tidak memberikan peringatan di C ++ 11 ( lihat langsung ).
2. Aturan sekuensing baru membuat i = ++ i +1; didefinisikan dengan baik dalam C ++ 11
Aturan pengurutan baru yang diadopsi setelah C ++ 03 berarti bahwa:
tidak lagi perilaku tidak terdefinisi dalam C ++ 11, ini tercakup dalam laporan cacat 637. Aturan pengurutan dan contoh tidak setuju
3. Aturan pengurutan baru juga membuat ++++ i; didefinisikan dengan baik dalam C ++ 11
Aturan pengurutan baru yang diadopsi setelah C ++ 03 berarti bahwa:
tidak lagi perilaku tidak terdefinisi dalam C ++ 11.
4. Sedikit Lebih Masuk Akal Tertanda-Kiri
Konsep selanjutnya dari C ++ 11 termasuk
N3485
yang saya tautkan di bawah ini memperbaiki perilaku yang tidak terdefinisi yaitu menggeser 1 bit menjadi atau melewati bit tanda . Ini juga tercakup dalam laporan cacat 1457 . Howard Hinnant mengomentari pentingnya perubahan pada utas tentang Apakah kiri-bergeser (<<) bilangan bulat negatif perilaku tidak terdefinisi dalam C ++ 11? .5. fungsi constexpr dapat diperlakukan sebagai kompilasi ekspresi konstanta waktu dalam C ++ 11
C ++ 11 memperkenalkan fungsi constexpr yang:
sementara C ++ 03 tidak memiliki fitur constexpr , kami tidak perlu menggunakan kata kunci constexpr secara eksplisit karena pustaka standar menyediakan banyak fungsi di C ++ 11 sebagai constexpr . Misalnya std :: numeric_limits :: min . Yang dapat menyebabkan perilaku berbeda, misalnya:
Menggunakan
clang
dalam C ++ 03 ini akan menyebabkanx
array panjang variabel, yang merupakan ekstensi dan akan menghasilkan peringatan berikut:sedangkan di C ++ 11
std::numeric_limits<unsigned int>::min()+2
adalah ekspresi konstanta waktu kompilasi dan tidak memerlukan ekstensi VLA.6. Dalam C ++ 11 spesifikasi pengecualian noexcept secara implisit dihasilkan untuk destruktor Anda
Karena dalam C ++ 11 destruktor yang ditentukan pengguna memiliki
noexcept(true)
spesifikasi implisit seperti yang dijelaskan dalam destruktor noexcept itu berarti bahwa program berikut:Di C ++ 11 akan memanggil
std::terminate
tetapi akan berhasil dijalankan di C ++ 03.7. Dalam C ++ 03, argumen templat tidak dapat memiliki tautan internal
Ini tercakup dengan baik di Mengapa std :: sort tidak menerima kelas Bandingkan dideklarasikan dalam suatu fungsi . Jadi kode berikut seharusnya tidak berfungsi di C ++ 03:
tetapi saat
clang
ini memungkinkan kode ini dalam mode C ++ 03 dengan peringatan kecuali Anda menggunakan-pedantic-errors
flag, yang agak menjijikkan, lihat langsung .8. >> tidak lagi salah bentuk saat menutup beberapa templat
Menggunakan
>>
untuk menutup beberapa templat tidak lagi salah bentuk tetapi dapat menyebabkan kode dengan hasil berbeda di C ++ 03 dan C + 11. Contoh di bawah ini diambil dari kurung siku kanan dan kompatibilitas ke belakang :dan hasilnya dalam C ++ 03 adalah:
dan di C ++ 11:
9. C ++ 11 mengubah beberapa konstruktor std :: vector
Kode yang sedikit dimodifikasi dari jawaban ini menunjukkan bahwa menggunakan konstruktor berikut dari std :: vector :
menghasilkan hasil yang berbeda di C ++ 03 dan C ++ 11:
10. Persempit konversi dalam inisialisasi agregat
Dalam C ++ 11 konversi penyempitan dalam inisialisasi agregat tidak terbentuk dan sepertinya
gcc
memungkinkan ini di C ++ 11 dan C ++ 03 meskipun itu memberikan peringatan secara default di C ++ 11:Ini dicakup dalam draft C ++ 11 bagian standar
8.5.4
Daftar-inisialisasi paragraf 3 :dan mengandung peluru berikut ( penekanan milikku ):
Ini dan banyak lagi contoh dibahas dalam rancangan C ++ bagian standar
annex C.2
C ++ dan ISO C ++ 2003 . Ini juga termasuk:Jenis baru string literal [...] Secara khusus, makro bernama R, u8, u8R, u, uR, U, UR, atau LR tidak akan diperluas ketika berdekatan dengan string literal tetapi akan ditafsirkan sebagai bagian dari string literal . Sebagai contoh
Dukungan string literal yang ditentukan pengguna [...] Sebelumnya, # 1 akan terdiri dari dua token pemroses yang terpisah dan makro _x akan diperluas. Dalam Standar Internasional ini, # 1 terdiri dari satu token pemrosesan awal, sehingga makro tidak diperluas.
Tentukan pembulatan untuk hasil bilangan bulat / dan% [...] kode 2003 yang menggunakan pembulatan bilangan bulat bulat hasilnya ke 0 atau ke arah infinity negatif, sedangkan Standar Internasional ini selalu membulatkan hasilnya ke 0.
Kompleksitas ukuran () fungsi anggota sekarang konstan [...] Beberapa implementasi wadah yang sesuai dengan C ++ 2003 mungkin tidak sesuai dengan persyaratan ukuran yang ditentukan () dalam Standar Internasional ini. Menyesuaikan wadah seperti std :: list dengan persyaratan yang lebih ketat mungkin memerlukan perubahan yang tidak kompatibel.
Ubah kelas dasar std :: ios_base :: failure [...] std :: ios_base :: failure tidak lagi diturunkan langsung dari std :: exception, tetapi sekarang berasal dari std :: system_error, yang pada gilirannya berasal dari std :: runtime_error. Kode C ++ 2003 yang valid yang mengasumsikan bahwa std :: ios_base :: failure diturunkan langsung dari std :: exception dapat dieksekusi secara berbeda dalam Standar Internasional ini.
sumber
Salah satu perubahan yang berpotensi tidak kompatibel ke belakang yang berbahaya adalah pada konstruktor kontainer berurutan seperti
std::vector
, khususnya dalam kelebihan menentukan ukuran awal. Di mana di C ++ 03, mereka menyalin elemen bawaan-dibangun, di C ++ 11 mereka default-membangun masing-masing.Pertimbangkan contoh ini (menggunakan
boost::shared_ptr
sehingga valid C ++ 03):C ++ 03 Contoh langsung
C ++ 11 Contoh langsung
Alasannya adalah bahwa C ++ 03 menetapkan satu kelebihan untuk "tentukan ukuran dan elemen prototipe" dan "tentukan ukuran saja," seperti ini (argumen pengalokasi dihilangkan karena singkatnya):
Ini akan selalu menyalin
prototype
kesize
kali wadah . Ketika dipanggil hanya dengan satu argumen, karenanya akan membuatsize
salinan dari elemen yang dibangun secara default.Di C ++ 11, tanda tangan konstruktor ini dihapus dan diganti dengan dua kelebihan ini:
Yang kedua berfungsi seperti sebelumnya, membuat
size
salinanprototype
elemen. Namun, yang pertama (yang sekarang menangani panggilan dengan hanya argumen ukuran yang ditentukan) default-membangun setiap elemen secara individual.Dugaan saya untuk alasan perubahan ini adalah bahwa kelebihan C ++ 03 tidak akan dapat digunakan dengan tipe elemen yang hanya bergerak. Tapi itu perubahan yang luar biasa, dan orang jarang mendokumentasikannya.
sumber
deque
sepuluh widget terpisah, bukan sepuluh widget berbagi sumber daya yang sama.Hasil pembacaan gagal dari
std::istream
telah berubah. CppReference merangkumnya dengan baik:Ini terutama masalah jika Anda terbiasa dengan semantik baru dan kemudian harus menulis menggunakan C ++ 03. Berikut ini bukan praktik yang sangat baik tetapi didefinisikan dengan baik dalam C ++ 11:
Namun, dalam C ++ 03, kode di atas menggunakan variabel yang tidak diinisialisasi dan dengan demikian memiliki perilaku yang tidak terdefinisi.
sumber
int x = 1, y = 1; cin >> x >> y; cout << x*y;
. Dengan C ++ 03, ini akan menghasilkan dengan benarx
ketika tidak ada yangy
bisa dibaca.Utas ini Perbedaan apa, jika ada, antara C ++ 03 dan C ++ 0x yang dapat dideteksi saat run-time memiliki contoh (disalin dari utas itu) untuk menentukan perbedaan bahasa, misalnya dengan mengeksploitasi referensi C ++ 11 yang runtuh:
dan c ++ 11 memungkinkan tipe lokal sebagai parameter templat:
sumber
Ini contoh lain:
Cetakan:
Lihat hasilnya di Coliru
sumber