ทำไม c ++ ไม่มี && = หรือ || = สำหรับบูลีน


126

มี"สิ่งที่เลวร้ายมาก"ที่สามารถเกิดขึ้น&&=และ||=ถูกใช้เป็นน้ำตาลในการสังเคราะห์bool foo = foo && barและbool foo = foo || bar?


5
ดูคำถามอื่น ๆ นี้: stackoverflow.com/questions/2324549/…อันนั้นเกี่ยวกับ Java แต่การแชร์เชื้อสาย C มักใช้อาร์กิวเมนต์เดียวกัน
jamesdlin

โดยพื้นฐานแล้ว c ++ ไม่มี b / c ที่พวกเขาไม่ได้ใส่ไว้ในภาษาเช่น Ruby มี boo ...
Kache

2
แต่ใน Ruby ไม่x ||= yเทียบเท่ากับ C ++ x = x ? x : y;สำหรับประเภทใด ๆ ? กล่าวอีกนัยหนึ่งคือ "ตั้งค่าเป็น y หากยังไม่ได้ตั้งค่า" มีประโยชน์มากกว่า C หรือ C ++ x ||= yมากซึ่ง (การ จำกัด ตัวดำเนินการมากเกินไป) จะทำ "ตั้งค่า x เป็น(bool)yเว้นแต่จะตั้งค่าไว้แล้ว" ฉันไม่กังวลที่จะเพิ่มผู้ให้บริการรายอื่นสำหรับสิ่งนั้นดูเหมือนว่าจะอ่อนแอเล็กน้อย if (!x) x = (bool)yเขียนเพียง แต่แล้วฉันไม่ได้ใช้boolตัวแปรมากพอที่จะต้องการตัวดำเนินการพิเศษที่มีประโยชน์จริงๆกับประเภทนั้นเท่านั้น
Steve Jessop

1
ฉันแน่ใจว่าเหตุผลหลักที่ C ++ ไม่มี&&=หรือ||=เพียงแค่ว่า C ไม่มี ฉันค่อนข้างแน่ใจว่าเหตุผลที่ C ไม่มีก็คือฟังก์ชันนี้ไม่ได้รับประโยชน์เพียงพอ
Jonathan Leffler

2
นอกจากนี้ยังเป็นอัลตร้าอวดความรู้สัญกรณ์bool foo = foo || bar;จะก่อให้เกิดพฤติกรรมที่ไม่ได้กำหนดเพราะไม่ได้เริ่มต้นก่อนที่จะมีการประเมินผลของfoo foo || barแน่นอนว่านี่เป็นสิ่งที่ต้องการbool foo = …initialization…; …; foo = foo || bar;และคำถามก็แสดงว่าถูกต้อง
Jonathan Leffler

คำตอบ:


73

A boolอาจเป็นtrueหรือfalseใน C ++ เท่านั้น ด้วยเหตุนี้การใช้งาน&=และ|=ค่อนข้างปลอดภัย (แม้ว่าฉันจะไม่ชอบสัญกรณ์เป็นพิเศษก็ตาม) จริงอยู่ที่พวกเขาจะดำเนินการบิตมากกว่าการดำเนินการเชิงตรรกะ (ดังนั้นจึงไม่ลัดวงจร) แต่การดำเนินการบิตเหล่านี้เป็นไปตามการแมปที่กำหนดไว้อย่างดีซึ่งเทียบเท่ากับการดำเนินการทางตรรกะได้อย่างมีประสิทธิภาพตราบใดที่ตัวถูกดำเนินการทั้งสองเป็นประเภทbool . 1

ตรงกันข้ามกับสิ่งที่คนอื่นพูดที่นี่boolใน C ++ จะต้องไม่มีค่าที่แตกต่างกันเช่น2. เมื่อกำหนดค่านั้นให้กับ a ค่าboolนั้นจะถูกแปลงtrueเป็นตามมาตรฐาน

วิธีเดียวที่จะทำให้ค่าที่ไม่ถูกต้องเป็นไฟล์ boolคือการใช้พreinterpret_castอยน์เตอร์:

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

แต่เนื่องจากรหัสนี้ส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนดไว้เราจึงสามารถเพิกเฉยต่อปัญหาที่อาจเกิดขึ้นนี้ได้อย่างปลอดภัยในการปฏิบัติตามรหัส C ++


1เป็นที่ยอมรับว่านี่เป็นข้อแม้ที่ค่อนข้างใหญ่เนื่องจากความคิดเห็นของ Angew แสดงให้เห็น:

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

เหตุผลก็คือb & 2ทำการส่งเสริมจำนวนเต็มเพื่อให้นิพจน์นั้นเทียบเท่ากับstatic_cast<int>(b) & 2ผลลัพธ์0ซึ่งจะถูกแปลงกลับเป็นไฟล์bool. ดังนั้นจึงเป็นเรื่องจริงที่ว่าการมีอยู่operator &&=จะช่วยปรับปรุงความปลอดภัยของประเภท


4
แต่ && และ || ตัวดำเนินการจะทำงานกับอะไรก็ได้ที่แปลงเป็นบูลไม่ใช่แค่บูล
dan04

13
พวกเขาไม่ทำสิ่งเดียวกันแม้ในบูล ||และ&&ทางลัดกล่าวคืออาร์กิวเมนต์ที่สองไม่ใช่ตัวถูกดำเนินการถ้าตัวถูกดำเนินการตัวแรกคือtrue(resp. falsefor &&) |, &, |=และ&=มักจะประเมินทั้งสองตัวถูกดำเนินการ
Niki

4
นี่ไม่ได้ตอบคำถามว่าทำไม && = และ || = ไม่ใช่ตัวดำเนินการ c ++
Thang

10
มันไม่ปลอดภัยที่จะใช้&=สำหรับด้านซ้ายมือของชนิดboolเพราะมันเป็นไปได้ที่ดีเลิศสำหรับด้านขวามือจะเป็นประเภทอื่น ๆ กว่าbool(เช่นislowerหรือฟังก์ชั่นอื่น STDLIB C ซึ่งผลตอบแทนที่ไม่ใช่ศูนย์สำหรับมูลค่าที่แท้จริง) ถ้าเรามีสมมุติฐาน&&=มันอาจจะบังคับให้ทางขวามือแปลงเป็นboolซึ่ง&=ไม่มี กล่าวอีกนัยหนึ่งbool b = true; b &= 2;ผลลัพธ์b == falseคือ
Angew ไม่ภูมิใจใน SO

2
@ แอนโทนิโอฝูงชนยาก 😝
Konrad Rudolph

45

&&และ&มีความหมายที่แตกต่างกันจะไม่ประเมินตัวถูกดำเนินการสองถ้าตัวถูกดำเนินการแรกคือ&& falseเช่น

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

ปลอดภัย แต่

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

boolไม่ได้แม้ว่าทั้งสองตัวถูกดำเนินการเป็นประเภท

เช่นเดียวกับ&=และ|=:

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

จะทำงานแตกต่างจาก:

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

35
เหตุผลอื่น ๆ ทั้งหมดที่มี && = ในความคิดของฉัน = P
Kache

4
นี่ไม่ใช่คำตอบจริงๆ
Catskul

27

คำตอบสั้น ๆ

ผู้ประกอบการทั้งหมด+=, -=, *=, /=, &=, |=... มีเลขคณิตและให้ความคาดหวังเดียวกัน

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

อย่างไรก็ตามผู้ประกอบการ&&=และ||=จะเป็นตรรกะและผู้ประกอบการเหล่านี้อาจจะผิดพลาดได้ง่ายเพราะนักพัฒนาจำนวนมากคาดว่าจะได้จะเรียกว่าเสมอในfoo()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
  • เราจำเป็นต้องทำให้ C / C ++ ซับซ้อนมากขึ้นเพื่อให้ได้คำสั่งลัดมาx = x && foo()หรือไม่?

  • เราต้องการทำให้คำสั่งที่คลุมเครือมากขึ้นx = x && foo()หรือไม่?
    หรือเราต้องการเขียนโค้ดที่มีความหมายเช่นif (x) x = foo();?


คำตอบยาว

ตัวอย่างสำหรับ &&=

หากมี&&=ตัวดำเนินการรหัสนี้:

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

เทียบเท่ากับ:

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

รหัสแรกนี้เกิดข้อผิดพลาดได้ง่ายเนื่องจากนักพัฒนาหลายคนคิดว่าf2()มักจะเรียกว่าf1()ค่าที่ส่งคืนเสมอ มันเหมือนกับการเขียนbool ok = f1() && f2();ที่f2()เรียกเฉพาะเมื่อf1()ส่งคืนtrueเท่านั้น

  • หากนักพัฒนาต้องการf2()ให้เรียกเมื่อf1()ส่งคืนเท่านั้นtrueดังนั้นโค้ดที่สองด้านบนจึงมีโอกาสเกิดข้อผิดพลาดน้อยกว่า
  • อย่างอื่น (ผู้พัฒนาต้องการf2()ให้เรียกเสมอ) &=ก็เพียงพอแล้ว:

ตัวอย่างสำหรับ &=

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

ยิ่งไปกว่านั้นคอมไพเลอร์จะปรับแต่งโค้ดด้านบนนี้ได้ง่ายกว่าโค้ดด้านล่าง:

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

เปรียบเทียบ &&และ&

เราอาจสงสัยว่าตัวดำเนินการ&&และ&ให้ผลลัพธ์เดียวกันหรือไม่เมื่อใช้กับboolค่าหรือไม่?

ตรวจสอบโดยใช้รหัส C ++ ต่อไปนี้:

#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);
}

เอาท์พุท:

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
======================

ข้อสรุป

ดังนั้นใช่เราสามารถแทนที่&&โดย&สำหรับboolค่า ;-)
เพื่อให้ดีขึ้นใช้แทน&= เราสามารถพิจารณาว่าไม่มีประโยชน์สำหรับบูลีน&&=
&&=

เหมือนกันสำหรับ ||=

ตัวดำเนินการ|=ยังมีโอกาสเกิดข้อผิดพลาดน้อยกว่า||=

หากต้องการf2()ให้เรียกนักพัฒนาเมื่อf1()ส่งคืนfalseเท่านั้นแทนที่จะเป็น:

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 ...

ฉันแนะนำทางเลือกที่เข้าใจได้มากขึ้นต่อไปนี้:

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)

หรือหากคุณต้องการทั้งหมดในรูปแบบบรรทัดเดียว :

// 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();

13
จะเป็นอย่างไรหากฉันต้องการพฤติกรรมนี้ การแสดงออกของมือขวาจะไม่ถูกดำเนินการหากการแสดงออกของมือซ้ายผิด มันน่ารำคาญที่ต้องเขียนตัวแปรสองครั้งเช่นsuccess = success && DoImportantStuff()
Niklas R

1
if(success) success = DoImportantStuff()คำแนะนำของฉันคือการเขียน หากคำสั่งที่success &&= DoImportantStuff()ได้รับอนุญาตให้นักพัฒนาจำนวนมากจะคิดว่าจะเรียกว่าเสมอสิ่งที่คุ้มค่าของDoImportantStuff() successหวังว่านี่จะตอบสิ่งที่คุณสงสัย ... ฉันได้ปรับปรุงคำตอบหลายส่วนแล้ว ช่วยบอกหน่อยว่าตอนนี้คำตอบของฉันเข้าใจได้มากขึ้นหรือยัง? (เกี่ยวกับจุดประสงค์ในการแสดงความคิดเห็นของคุณ) ไชโยเจอกัน ;-)
olibre

9
"หากคำสั่งsuccess &&= DoImportantStuff()นั้นได้รับอนุญาตนักพัฒนาหลายคนจะคิดDoImportantStuff()ว่าสิ่งที่เรียกว่าคุณค่าของความสำเร็จเสมอ" คุณสามารถพูดได้ว่าเกี่ยวกับif (success && DoImportantStuff())ว่า &&=ตราบใดที่พวกเขาจำตรรกะที่อยู่เบื้องหลังถ้าไวยากรณ์พวกเขาควรจะมีปัญหากับการไม่มี
pilkch

2
ฉันไม่เห็นว่าคนอาจคิดว่าf1()มักจะประเมินในแต่จะไม่ถือว่ามันมักจะประเมินในok &&= f(1) ok = ok && f(1)ดูเหมือนจะเป็นไปได้สำหรับฉัน
einpoklum

1
จริง ๆ แล้วฉันคาดว่า v1 += e2จะเป็นน้ำตาลเชิงไวยากรณ์ที่เทียบเท่าv1 = v1 + e1กับตัวแปร v1 และนิพจน์ e2 แค่สัญกรณ์ชวเลขนั่นคือทั้งหมด
einpoklum
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.