Mengapa c ++ tidak memiliki && = atau || = untuk boolean?

126

Apakah ada "hal yang sangat buruk" yang dapat terjadi &&=dan ||=digunakan sebagai gula sintaksis untuk bool foo = foo && bardan bool foo = foo || bar?

Kache
sumber
5
Lihat pertanyaan lain ini: stackoverflow.com/questions/2324549/… Yang itu tentang Java, tetapi berbagi silsilah C, argumen yang sama kebanyakan berlaku.
jamesdlin
Pada dasarnya, hanya c ++ yang tidak memilikinya b / c mereka tidak memasukkannya - bahasa seperti Ruby memilikinya. boo ...
Kache
2
Tapi di Ruby, bukankah x ||= ykira - kira setara dengan C ++ x = x ? x : y;untuk semua tipe? Dengan kata lain, "setel ke y jika belum disetel". Itu jauh lebih berguna daripada C atau C ++ x ||= y, yang (pemblokiran operator yang berlebihan) akan melakukan "setel x ke (bool)ykecuali sudah disetel". Saya tidak ingin menambahkan operator lain untuk itu, sepertinya agak lemah. Tulis saja if (!x) x = (bool)y. Tapi kemudian, saya tidak benar-benar menggunakan boolvariabel cukup untuk menginginkan operator tambahan yang hanya benar-benar berguna dengan satu jenis itu.
Steve Jessop
1
Saya yakin alasan utama C ++ tidak memiliki &&=atau ||=hanya karena C tidak memilikinya. Saya cukup yakin alasan C tidak memilikinya adalah karena fungsinya dianggap tidak cukup menguntungkan.
Jonathan Leffler
2
Juga, karena sangat pedantic, notasi tersebut bool foo = foo || bar;akan memunculkan perilaku tidak terdefinisi karena footidak diinisialisasi sebelum evaluasi foo || bar. Tentu saja, ini dimaksudkan sebagai sesuatu yang disukai bool foo = …initialization…; …; foo = foo || bar;dan pertanyaannya kemudian menjadi valid.
Jonathan Leffler

Jawaban:

73

A boolmungkin hanya trueatau falsedalam C ++. Dengan demikian, menggunakan &=dan |=relatif aman (meskipun saya tidak terlalu menyukai notasinya). Benar, mereka akan melakukan operasi bit daripada operasi logis (dan dengan demikian mereka tidak akan mengalami hubungan pendek) tetapi operasi bit ini mengikuti pemetaan yang ditentukan dengan baik, yang secara efektif setara dengan operasi logis, selama kedua operan adalah tipebool . 1

Bertentangan dengan apa yang orang lain katakan di sini, booldalam C ++ tidak boleh memiliki nilai yang berbeda seperti 2. Saat menetapkan nilai itu ke a bool, itu akan diubah menjadi truesesuai standar.

Satu-satunya cara untuk memasukkan nilai yang tidak valid menjadi a booladalah dengan menggunakan reinterpret_caston pointer:

int i = 2;
bool b = *reinterpret_cast<bool*>(&i);
b |= true; // MAY yield 3 (but doesn’t on my PC!)

Tetapi karena kode ini menghasilkan perilaku yang tidak terdefinisi, kami dapat dengan aman mengabaikan potensi masalah ini dalam menyesuaikan kode C ++.


1 Harus diakui, ini adalah peringatan yang cukup besar seperti yang digambarkan oleh komentar Angew:

bool b = true;
b &= 2; // yields `false`.

Alasannya adalah yang b & 2melakukan promosi integer sedemikian rupa sehingga ekspresi tersebut kemudian setara dengan static_cast<int>(b) & 2, yang menghasilkan 0, yang kemudian diubah kembali menjadi a bool. Jadi memang benar keberadaan sebuah operator &&=akan meningkatkan keamanan tipe.

Konrad Rudolph
sumber
4
Tapi && dan || operator akan mengerjakan apa pun yang diubah menjadi bool, bukan hanya bool.
dan04
13
Mereka tidak melakukan hal yang sama, bahkan pada pelacur. ||dan &&shortcut, yaitu argumen kedua bukan operan jika operan pertama adalah true(resp. falsefor &&). |, &, |=Dan &=selalu mengevaluasi kedua operan.
Niki
4
ini tidak menjawab pertanyaan mengapa && = dan || = bukan operator c ++.
thang
9
Ini tidak aman untuk digunakan &=untuk sisi kiri dari jenis bool, karena itu sangat mungkin untuk sisi kanan menjadi tipe selain bool(seperti isloweratau fungsi stdlib C lain yang kembali nol untuk nilai sebenarnya). Jika kita memiliki hipotesis &&=, itu mungkin akan memaksa sisi kanan untuk beralih ke bool, yang &=tidak. Dengan kata lain, bool b = true; b &= 2;menghasilkan b == false.
Angew tidak lagi bangga dengan SO
2
@Anio Tough kerumunan. 😝
Konrad Rudolph
44

&&dan &memiliki semantik yang berbeda: &&tidak akan mengevaluasi operan kedua jika operan pertama adalah false. yaitu sesuatu seperti

flag = (ptr != NULL) && (ptr->member > 3);

aman, tapi

flag = (ptr != NULL) & (ptr->member > 3);

tidak, meskipun kedua operan memiliki tipe bool.

Hal yang sama berlaku untuk &=dan |=:

flag = CheckFileExists();
flag = flag && CheckFileReadable();
flag = flag && CheckFileContents();

akan berperilaku berbeda dari:

flag = CheckFileExists();
flag &= CheckFileReadable();
flag &= CheckFileContents();
Niki
sumber
35
lebih banyak alasan untuk memiliki && = menurut saya. = P
Kache
4
Ini sebenarnya bukan jawaban.
Catskul
27

Jawaban singkat

Semua operator +=, -=, *=, /=, &=, |=... adalah aritmatika dan memberikan harapan yang sama:

x &= foo()  // We expect foo() be called whatever the value of x

Namun, operator &&=dan ||=akan logis, dan operator ini mungkin rawan kesalahan karena banyak pengembang akan foo()selalu dipanggil x &&= foo().

bool x;
// ...
x &&= foo();           // Many developers might be confused
x = x && foo();        // Still confusing but correct
x = x ? foo() : x;     // Understandable
x = x ? foo() : false; // Understandable
if (x) x = foo();      // Obvious
  • Apakah kita benar-benar perlu membuat C / C ++ lebih kompleks untuk mendapatkan jalan pintas x = x && foo()?

  • Apakah kita benar-benar ingin lebih mengaburkan pernyataan samar x = x && foo()?
    Atau apakah kita ingin menulis kode yang bermakna seperti if (x) x = foo();?


Jawaban panjang

Contoh untuk &&=

Jika &&=operator tersedia, maka kode ini:

bool ok = true; //becomes false when at least a function returns false
ok &&= f1();
ok &&= f2(); //we may expect f2() is called whatever the f1() returned value

setara dengan:

bool ok = true;
if (ok) ok = f1();
if (ok) ok = f2(); //f2() is called only when f1() returns true

Kode pertama ini rawan error karena banyak developer akan mengira f2()selalu dipanggil berapapun nilai yang f1()dikembalikan. Ini seperti menulis di bool ok = f1() && f2();mana f2()dipanggil hanya ketika f1()kembali true.

  • Jika pengembang sebenarnya hanya ingin f2()dipanggil saat f1()kembali true, maka kode kedua di atas tidak terlalu rentan terhadap kesalahan.
  • Lain (pengembang ingin f2()selalu dipanggil), &=cukup:

Contoh untuk &=

bool ok = true;
ok &= f1();
ok &= f2(); //f2() always called whatever the f1() returned value

Selain itu, lebih mudah bagi kompiler untuk mengoptimalkan kode di atas daripada kode di bawah ini:

bool ok = true;
if (!f1())  ok = false;
if (!f2())  ok = false;  //f2() always called

Bandingkan &&dan&

Kita mungkin bertanya-tanya apakah operator &&dan &memberikan hasil yang sama bila diterapkan pada boolnilai?

Mari kita periksa menggunakan kode C ++ berikut:

#include <iostream>

void test (int testnumber, bool a, bool b)
{
   std::cout << testnumber <<") a="<< a <<" and b="<< b <<"\n"
                "a && b = "<< (a && b)  <<"\n"
                "a &  b = "<< (a &  b)  <<"\n"
                "======================"  "\n";
}

int main ()
{
    test (1, true,  true);
    test (2, true,  false);
    test (3, false, false);
    test (4, false, true);
}

Keluaran:

1) a=1 and b=1
a && b = 1
a &  b = 1
======================
2) a=1 and b=0
a && b = 0
a &  b = 0
======================
3) a=0 and b=0
a && b = 0
a &  b = 0
======================
4) a=0 and b=1
a && b = 0
a &  b = 0
======================

Kesimpulan

Oleh karena itu YA kita dapat mengganti &&dengan &untuk boolnilai ;-)
Jadi lebih baik gunakan &=daripada &&=.
Kami bisa mempertimbangkan&&= tidak berguna bagi boolean.

Sama untuk ||=

Operator |=juga kurang rawan kesalahan dari||=

Jika pengembang ingin f2()dipanggil hanya saat f1()kembali false, bukan:

bool ok = false;
ok ||= f1();
ok ||= f2(); //f2() is called only when f1() returns false
ok ||= f3(); //f3() is called only when f1() or f2() return false
ok ||= f4(); //f4() is called only when ...

Saya menyarankan alternatif yang lebih mudah dipahami berikut ini:

bool ok = false;
if (!ok) ok = f1();
if (!ok) ok = f2();
if (!ok) ok = f3();
if (!ok) ok = f4();
// no comment required here (code is enough understandable)

atau jika Anda lebih suka gaya semua dalam satu baris :

// this comment is required to explain to developers that 
// f2() is called only when f1() returns false, and so on...
bool ok = f1() || f2() || f3() || f4();
olibre.dll
sumber
13
Bagaimana jika saya benar-benar menginginkan perilaku ini? Bahwa ekspresi tangan kanan tidak dieksekusi jika ekspresi tangan kiri salah. Menjengkelkan untuk menulis variabel dua kali, sepertisuccess = success && DoImportantStuff()
Niklas R
1
Saran saya adalah menulis if(success) success = DoImportantStuff(). Jika pernyataan success &&= DoImportantStuff()itu diizinkan, banyak pengembang akan menganggap DoImportantStuff()selalu disebut apa pun nilainya success. Semoga ini menjawab pertanyaan Anda ... Saya juga telah meningkatkan banyak bagian dari jawaban saya. Tolong beri tahu saya jika jawaban saya lebih bisa dimengerti sekarang? (tentang tujuan komentar Anda) Cheers, See you ;-)
olibre
9
"Jika pernyataan itu success &&= DoImportantStuff() itu dibiarkan, banyak developer akan mengira DoImportantStuff()selalu menyebut apa pun nilai kesuksesannya." Anda bisa mengatakan itu if (success && DoImportantStuff()). Selama mereka mengingat logika di balik sintaks if, mereka seharusnya tidak mengalami masalah &&=.
pilkch
2
Saya tidak melihat bagaimana orang mungkin berasumsi bahwa f1()selalu mengevaluasi dalam ok &&= f(1) tetapi tidak akan menganggap itu selalu mengevaluasi dalam ok = ok && f(1). Sepertinya bagi saya.
einpoklum
1
Saya benar-benar berharap v1 += e2menjadi gula sintaksis yang setara v1 = v1 + e1untuk variabel v1 dan ekspresi e2. Hanya notasi steno, itu saja.
einpoklum