Mengapa std :: swap tidak bekerja pada elemen vektor <bool> di bawah Dentang / Menang?

14

Saya punya kode seperti ini:

#include <vector>
#include <utility>

int main()
{
   std::vector<bool> vb{true, false};
   std::swap(vb[0], vb[1]);
}

Argumen tentang kewarasan vector<bool>samping, ini bekerja dengan baik pada:

  • Dentang untuk Mac
  • Visual Studio untuk Windows
  • GCC untuk Linux

Kemudian saya mencoba membangunnya dengan Dentang pada Windows dan menerima kesalahan berikut (diringkas):

error: no matching function for call to 'swap'
                                std::swap(vb[0], vb[1]);
                                ^~~~~~~~~

note: candidate function [with _Ty = std::_Vb_reference<std::_Wrap_alloc<std::allocator<unsigned int> > >, $1 = void] not viable: expects an l-value for 1st argument
inline void swap(_Ty& _Left, _Ty& _Right) _NOEXCEPT_COND(is_nothrow_move_constructible_v<_Ty>&&

Saya terkejut bahwa hasilnya berbeda di seluruh implementasi.

Mengapa tidak bekerja dengan Dentang di Windows?

Lightness Races di Orbit
sumber
Jadi saya kira klarifikasi yang dibutuhkan adalah: Apakah hasil dari operator[]nilai yang lebih rendah? dan dapat std::swapberoperasi pada nilai dan nilai?
Mgetz
@Mgetz Ya. Tidak. Dalam urutan itu. Pertanyaan ini ditanyakan "nyata" secara pribadi tempo hari, dan saya pikir cukup menghibur bahwa jawabannya adalah "Dentang / Menang tidak rusak; kode ini dipecahkan sepanjang waktu ini tetapi kombo toolchain utama tidak pernah repot-repot memberi tahu Anda "untuk menuliskannya di sini: P
Lightness Races di Orbit
2
Sama seperti FYI, ini tidak dapat dikompilasi dalam VS 2019 dengan /permissive-(konformitas), yang umumnya harus tetap digunakan;)
ChrisMM
1
@ ChrisMM Memang! Moda kesesuaian dimatikan adalah bagian dari teka-teki. (Meskipun kami tidak tahu itu sebelum melihat ke dalamnya!) Dan jawaban saya menunjukkan bahwa: P
Lightness Races in Orbit

Jawaban:

15

Standar tidak mengharuskan ini untuk dikompilasi di rantai alat apa pun !

Ingat pertama yang vector<bool>aneh dan berlangganan itu memberi Anda objek sementara dari jenis proxy yang disebut std::vector<bool>::reference, bukan yang sebenarnya bool&.

Pesan kesalahan memberitahu Anda bahwa itu tidak dapat mengikat ini sementara untuk constreferensi non- nilai dalam template <typename T> std::swap(T& lhs, T& rhs)implementasi generik .

Ekstensi!

Namun, ternyata libstdc ++ mendefinisikan overload untuk std::swap(std::vector<bool>::reference, std::vector<bool>::reference), tetapi ini merupakan perluasan ke standar (atau, jika ada di sana, saya tidak dapat menemukan bukti untuk itu).

libc ++ juga melakukan ini .

Saya kira implementasi Visual Studio stdlib, yang masih Anda gunakan, tidak , tapi kemudian untuk menambah penghinaan Anda dapat mengikat temporaries ke nilai referensi di VS (kecuali jika Anda menggunakan mode kesesuaian), jadi std::swapfungsi standar, "generik", berfungsi sampai Anda mengganti kompiler VS untuk kompiler Dentang yang lebih ketat.

Akibatnya, Anda telah mengandalkan ekstensi pada ketiga rantai alat yang berfungsi untuk Anda, dan kombinasi Dentang Windows adalah satu-satunya yang benar-benar menunjukkan kepatuhan yang ketat.

(Menurut pendapat saya, ketiga alat itu seharusnya mendiagnosis hal ini sehingga Anda tidak mengirimkan kode non-portabel selama ini. 😊)

Apa sekarang?

Mungkin tergoda untuk menambahkan spesialisasi Anda sendiri std::swapdan std::vector<bool>::reference, tetapi Anda tidak diizinkan melakukan ini untuk tipe standar; memang, itu akan bertentangan dengan kelebihan yang libstdc ++ dan libc ++ telah memilih untuk ditambahkan sebagai ekstensi.

Jadi, agar portabel dan sesuai, Anda harus mengubah kode Anda .

Mungkin yang kuno:

const bool temp = vb[0];
vb[0] = vb[1];
vb[1] = temp;

Atau manfaatkan fungsi anggota statis khusus yang melakukan persis seperti yang Anda inginkan :

std::vector<bool>::swap(vb[0], vb[1]);

Dapat juga dieja sebagai berikut:

vb.swap(vb[0], vb[1]);
Lightness Races di Orbit
sumber
Mengenai tetapi tidak seharusnya AFAIK mereka diizinkan untuk melakukannya. Selama mereka tidak melanggar kode yang sesuai mereka dapat memperpanjang implementasi untuk membuat kode yang rusak "OK".
NathanOliver
@ NathanOliver-ReinstateMonica Baiklah, oke. Bukankah mereka setidaknya harus mendiagnosis penggunaan hal-hal seperti itu? eel.is/c++draft/intro.compliance#8
Lightness Races in Orbit
@LightnessRaceswithMonica apakah ada bahasa yang melarang ekstensi ini?
Mgetz
@Mgetz Maaf, saya tidak fasih dalam semua bahasa yang masih ada jadi saya tidak bisa menjawabnya
Lightness Races in Orbit
Saya tidak yakin apakah menggunakan ekstensi seperti itu yang tidak sesuai dengan dokumen ini berlaku. Mereka menambahkan kelebihan yang dibutuhkan std::vector<bool>::referencesehingga tidak ada yang benar-benar buruk. Bagi saya kedengarannya seperti menggunakan sesuatu seperti char * foo = "bar";akan membutuhkan diagnostik karena itu tidak terbentuk.
NathanOliver