Tại sao c ++ không có && = hoặc || = cho booleans?


126

Có một "điều rất xấu" có thể xảy ra &&=||=được sử dụng làm đường cú pháp cho bool foo = foo && barbool foo = foo || bar?


5
Xem câu hỏi khác này: stackoverflow.com/questions/2324549/ Google Đó là về Java, nhưng chia sẻ dòng C, các đối số tương tự chủ yếu được áp dụng.
jamesdlin

Về cơ bản, chỉ c ++ không có nó vì họ không đưa nó vào - những ngôn ngữ như Ruby có nó. boo ...
Kache

2
Nhưng trong Ruby, không x ||= ytương đương với C ++ x = x ? x : y;cho bất kỳ loại nào? Nói cách khác, "đặt thành y nếu chưa được đặt". Điều đó hữu ích hơn đáng kể so với C hoặc C ++ x ||= y, điều này (quá tải toán tử chặn) sẽ thực hiện "đặt x thành (bool)ytrừ khi đã được đặt". Tôi không lo lắng để thêm một nhà điều hành cho điều đó, nó có vẻ hơi yếu. Chỉ cần viết if (!x) x = (bool)y. Nhưng sau đó, tôi không thực sự sử dụng boolcác biến đủ để muốn các toán tử bổ sung chỉ thực sự hữu ích với loại đó.
Steve Jessop

1
Tôi chắc chắn lý do chính khiến C ++ không có &&=hoặc ||=đơn giản là C không có chúng. Tôi chắc chắn rằng lý do C không có chúng là chức năng không được coi là đủ lợi ích.
Jonathan Leffler

2
Ngoài ra, là cực kỳ mô phạm, ký hiệu bool foo = foo || bar;sẽ gọi hành vi không xác định vì fookhông được khởi tạo trước khi đánh giá foo || bar. Tất nhiên, điều này được dự định là một cái gì đó như thế bool foo = …initialization…; …; foo = foo || bar;và câu hỏi sau đó là hợp lệ.
Jonathan Leffler

Câu trả lời:


73

A boolchỉ có thể truehoặc falsetrong C ++. Như vậy, sử dụng &=|=tương đối an toàn (mặc dù tôi không đặc biệt thích ký hiệu này). Đúng, chúng sẽ thực hiện các hoạt động bit thay vì các hoạt động logic (và do đó chúng sẽ không bị đoản mạch) nhưng các hoạt động bit này tuân theo ánh xạ được xác định rõ, tương đương với các hoạt động logic, miễn là cả hai toán hạng đều thuộc loạibool . 1

Trái ngược với những gì người khác đã nói ở đây, a booltrong C ++ không bao giờ phải có giá trị khác như 2. Khi gán giá trị đó cho a bool, nó sẽ được chuyển đổi truetheo tiêu chuẩn.

Cách duy nhất để có được một giá trị không hợp lệ boollà bằng cách sử dụng reinterpret_casttrên các con trỏ:

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

Nhưng vì mã này dẫn đến hành vi không xác định dù sao đi nữa, chúng tôi có thể bỏ qua vấn đề tiềm ẩn này trong việc tuân thủ mã C ++.


1 Phải thừa nhận rằng đây là một cảnh báo khá lớn như nhận xét của Angew minh họa:

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

Lý do là b & 2thực hiện quảng cáo số nguyên sao cho biểu thức sau đó tương đương với static_cast<int>(b) & 2, kết quả 0là, sau đó được chuyển đổi trở lại thành a bool. Vì vậy, sự thật là sự tồn tại của một loại operator &&=sẽ cải thiện an toàn loại.


4
Nhưng && và || toán tử sẽ làm việc trên bất cứ thứ gì chuyển đổi thành bool, không chỉ bool.
dan04

13
Họ không làm điều tương tự, ngay cả trên các bool. ||&&phím tắt, tức là đối số thứ hai không phải là toán hạng nếu toán hạng thứ nhất là true(tương ứng falsevới &&). |, &, |=&=luôn luôn đánh giá cả hai toán hạng.
Niki

4
điều này không trả lời câu hỏi tại sao && = và || = không phải là toán tử c ++.
thang

9
Đó là không an toàn để sử dụng &=cho một bên trái tay loại bool, bởi vì nó hoàn toàn có thể cho phía bên tay phải là kiểu khác hơn bool(ví dụ như islowernày hay cách khác chức năng stdlib C mà lợi nhuận khác không cho giá trị true). Nếu chúng ta có giả thuyết &&=, có lẽ nó sẽ buộc phía bên phải chuyển đổi sang bool, điều &=này không. Nói cách khác, bool b = true; b &= 2;kết quả trong b == false.
Angew không còn tự hào về SO

2
@Antonio Đám đông khó khăn. 😝
Konrad Rudolph

44

&&&có ngữ nghĩa khác nhau: &&sẽ không đánh giá toán hạng thứ hai nếu toán hạng thứ nhất là false. tức là một cái gì đó như

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

là an toàn, nhưng

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

là không, mặc dù cả hai toán hạng đều thuộc loại bool.

Điều này cũng đúng với &=|=:

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

sẽ hành xử khác với:

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

35
tất cả lý do nhiều hơn để có && = theo ý kiến ​​của tôi. = P
Kache

4
Đây thực sự không phải là một câu trả lời.
Catskul

27

Câu trả lời ngắn

Tất cả các nhà khai thác +=, -=, *=, /=, &=, |=... là số học và cung cấp cùng sự mong đợi:

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

Tuy nhiên, các toán tử &&=||=sẽ hợp lý và các toán tử này có thể dễ bị lỗi vì nhiều nhà phát triển mong đợi foo()sẽ luôn được gọi 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
  • Chúng ta có thực sự cần phải làm cho C / C ++ phức tạp hơn nữa để có một lối tắt cho x = x && foo()không?

  • Chúng ta có thực sự muốn làm xáo trộn thêm tuyên bố khó hiểu x = x && foo()không?
    Hay chúng ta muốn viết mã có ý nghĩa như thế if (x) x = foo();nào?


Câu trả lời dài

Ví dụ như &&=

Nếu &&=toán tử có sẵn, thì mã này:

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

tương đương với:

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

Mã đầu tiên này dễ bị lỗi vì nhiều nhà phát triển sẽ nghĩ f2()luôn được gọi là bất cứ f1()giá trị nào được trả về. Nó giống như viết bool ok = f1() && f2();nơi f2()chỉ được gọi khi f1()trở về true.

  • Nếu nhà phát triển thực sự muốn f2()được gọi chỉ khi f1()trả về true, do đó mã thứ hai ở trên ít bị lỗi hơn.
  • Khác (nhà phát triển muốn f2()luôn được gọi), &=là đủ:

Ví dụ như &=

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

Hơn nữa, trình biên dịch dễ dàng tối ưu hóa mã trên này hơn mã bên dưới:

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

So sánh &&&

Chúng tôi có thể tự hỏi liệu các toán tử &&&cho kết quả tương tự khi áp dụng trên boolcác giá trị?

Hãy kiểm tra bằng mã C ++ sau:

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

Đầu ra:

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

Phần kết luận

Do đó CÓ, chúng ta có thể thay thế &&bằng &các boolgiá trị ;-)
Vì vậy, sử dụng tốt hơn &=thay vì &&=.
Chúng ta có thể coi &&=là vô dụng đối với booleans.

Giống với ||=

Toán tử |=cũng ít bị lỗi hơn||=

Nếu nhà phát triển muốn f2()được gọi chỉ khi f1()trả về false, thay vì:

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

Tôi khuyên bạn nên thay thế dễ hiểu hơn sau đây:

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)

hoặc nếu bạn thích tất cả trong một kiểu đường :

// 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
Nếu tôi thực sự muốn hành vi này thì sao? Rằng biểu thức tay phải không được thực hiện nếu biểu thức tay trái sai. Thật khó chịu khi viết các biến hai lần, nhưsuccess = success && DoImportantStuff()
Niklas R

1
Lời khuyên của tôi là viết if(success) success = DoImportantStuff(). Nếu tuyên bố success &&= DoImportantStuff()được cho phép, nhiều nhà phát triển sẽ nghĩ DoImportantStuff()luôn được gọi là bất cứ giá trị nào success. Hy vọng câu trả lời này những gì bạn tự hỏi ... Tôi cũng đã cải thiện nhiều phần của câu trả lời của tôi. Xin vui lòng cho tôi biết nếu câu trả lời của tôi là dễ hiểu hơn bây giờ? (về mục đích bình luận của bạn) Chúc mừng, Hẹn gặp lại ;-)
olibre

9
"Nếu tuyên bố success &&= DoImportantStuff()được cho phép, nhiều nhà phát triển sẽ nghĩ DoImportantStuff()luôn được gọi là bất cứ giá trị nào của thành công." Bạn có thể nói rằng về if (success && DoImportantStuff())mặc dù. Miễn là họ nhớ logic đằng sau cú pháp if, họ sẽ không gặp rắc rối với &&=.
pilkch

2
Tôi không thấy mọi người có thể cho rằng f1()luôn đánh giá như thế nào ok &&= f(1) nhưng sẽ không cho rằng nó luôn luôn đánh giá ok = ok && f(1). Có vẻ như có khả năng với tôi.
einpoklum 2/2/2016

1
Tôi thực sự mong đợi v1 += e2là đường cú pháp tương đương v1 = v1 + e1với biến v1 và biểu thức e2. Chỉ cần một ký hiệu tốc ký, thế thôi.
einpoklum
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.