2013-07-27 35 views
35

Tôi đã thấy mã này:Cách sử dụng hợp lý toán tử dấu phẩy là gì?

if (cond) { 
    perror("an error occurred"), exit(1); 
} 

Tại sao bạn làm điều đó? Tại sao không chỉ:

if (cond) { 
    perror("an error occurred"); 
    exit(1); 
} 
+3

Toán tử dấu phẩy là vô dụng ngoài biểu thức SFINAE. – Rapptz

+13

Toán tử dấu phẩy đôi khi hữu ích trong các ngữ cảnh như phần thân của macro mà bạn muốn kết hợp nhiều thao tác vào một câu lệnh đơn. Nếu không, nó có thể hữu ích khi tăng hai biến trong một vòng lặp hoặc trong một vài vị trí cách điệu khác. Nói chung, mặc dù, nó là để tránh; một dấu chấm phẩy tốt hơn dấu phẩy. Xem thêm các câu hỏi như [Ưu tiên toán tử dấu phẩy trong khi được sử dụng với toán tử '?:'] (Http://stackoverflow.com/questions/16854007/comma-operator-precedence-while-used-with-operator) cho các ví dụ về sự nhầm lẫn do toán tử dấu phẩy gây ra. –

+2

@JonathanLeffler ',' chúng tôi cũng thường xuyên sử dụng cho vòng lặp –

Trả lời

55

Trong ví dụ của bạn, nó không có lý do gì cả. Đó là vào dịp hữu ích khi viết như

if(cond) 
    perror("an error occured"), exit(1) ; 

- sau đó bạn không cần dấu ngoặc nhọn. Nhưng đó là một lời mời đến thảm họa.

Toán tử dấu phẩy là đặt hai hoặc nhiều biểu thức ở vị trí mà tham chiếu chỉ cho phép một. Trong trường hợp của bạn, không cần phải sử dụng nó; trong các trường hợp khác, chẳng hạn như trong một vòng lặp while, nó có thể hữu ích:

while (a = b, c < d) 
    ... 

trong đó "đánh giá" thực tế của vòng lặp while chỉ được điều chỉnh trên biểu thức cuối cùng.

+31

Nói cách khác, toán tử dấu phẩy chủ yếu hữu ích cho việc làm xáo trộn. –

+7

Toán tử dấu phẩy kết hợp hai hoặc nhiều * biểu thức *, chứ không phải câu lệnh. –

+0

@Keith: ta, bạn đúng. Đã chỉnh sửa. – usr2564301

19

Trường hợp hợp pháp của toán tử dấu phẩy rất hiếm, nhưng chúng tồn tại. Một ví dụ là khi bạn muốn có một cái gì đó xảy ra bên trong một đánh giá có điều kiện. Ví dụ:

std::wstring example; 
auto it = example.begin(); 
while (it = std::find(it, example.end(), L'\\'), it != example.end()) 
{ 
    // Do something to each backslash in `example` 
} 

Nó cũng có thể được sử dụng ở những nơi bạn chỉ có thể đặt một biểu thức, nhưng muốn hai điều xảy ra. Ví dụ, gia vòng sau x và y suất này trong cho thành phần thứ ba vòng của:

int x = 0; 
int y = some_number; 
for(; x < y; ++x, --y) 
{ 
    // Do something which uses a converging x and y 
} 

Đừng đi tìm kiếm công dụng của nó, nhưng nếu nó là thích hợp, đừng ngại sử dụng nó, và không được ném cho một vòng lặp nếu bạn nhìn thấy người khác sử dụng nó. Nếu bạn có hai thứ không có lý do để không phải là các câu lệnh riêng biệt, hãy tạo các câu lệnh riêng biệt thay vì sử dụng toán tử dấu phẩy.

+1

Billy, không phải là kết quả của một nhiệm vụ giá trị mới nhất của nó là gì? Vì bạn đang đánh giá lại 'nó' ngay sau khi gán, bạn có thể thêm phép thử mà không cần toán tử dấu phẩy. (Đó là một ví dụ hợp lệ.) – usr2564301

+1

@Jongware: Có, trong trường hợp cụ thể đó bạn có thể làm điều đó. Cá nhân, tôi tìm thấy dấu phẩy dễ đọc hơn là đặt các bài tập trong điều kiện (vì khả năng gây nhầm lẫn '=' so với '=='). Nhưng đó là một lựa chọn phong cách. –

+0

Ta. Tôi thường cố gắng tránh * cả hai * công trình vì mục đích dễ đọc ;-) – usr2564301

4

Toán tử dấu phẩy cho phép biểu thức nhóm nơi biểu tượng được mong đợi.

Ví dụ nó có thể hữu ích trong một số trường hợp:

// In a loop 
while (a--, a < d) ... 

Nhưng trong trường hợp bạn không có lý do để sử dụng nó. Nó sẽ gây nhầm lẫn ... đó là nó ...

Trong trường hợp của bạn, nó chỉ là để tránh dấu ngoặc nhọn:

if(cond) 
    perror("an error occured"), exit(1); 

// => 
if (cond) 
{ 
    perror("an error occured"); 
    exit(1); 
} 

Một liên kết đến một tài liệu hướng dẫn comma operator.

+0

Ví dụ thứ hai của bạn ('int a = 4, b = 5;') không được gán nhưng khởi tạo, toán tử không phải là toán tử dấu phẩy (đối với tất cả có dấu phẩy tách hai định nghĩa) –

+0

@JonathanLeffler Bạn đúng, chỉ cần xóa nó –

1

Trong trường hợp của bạn, toán tử dấu phẩy là vô ích vì nó có thể đã được sử dụng để tránh curly braces, nhưng không phải như vậy vì nhà văn đã đặt chúng. Do đó, vô dụng và có thể gây nhầm lẫn.

4

Việc sử dụng chính của toán tử dấu phẩy là obfuscation; nó cho phép làm hai số những thứ mà người đọc chỉ mong đợi một. Một trong những cách thường gặp nhất sử dụng — thêm tác dụng phụ vào một điều kiện, nằm trong danh mục này.Có một vài trường hợp mà có thể được coi là hợp lệ, tuy nhiên :

Người được sử dụng để trình bày nó trong K & R: incrementing hai biến trong một vòng lặp for. Trong mã hiện đại, điều này có thể xảy ra trong một hàm như std::transform hoặc std::copy, trong đó bộ lặp đầu ra được tăng lên đồng thời với trình lặp đầu vào. (Thông thường, trong số , các hàm này sẽ chứa một vòng lặp while, với số gia tăng trong các câu lệnh riêng biệt ở cuối vòng lặp. Trong trường hợp này , không có điểm nào trong việc sử dụng dấu phẩy thay vì hai câu lệnh.)

một trường hợp khác mà nói đến cái tâm là xác nhận dữ liệu đầu vào thông số trong một danh sách initializer: (. điều này giả định rằng validate(param) sẽ ném một ngoại lệ nếu cái gì là sai)

MyClass::MyClass(T const& param) 
    : member((validate(param), param)) 
{ 
} 

sử dụng này không phải là đặc biệt hấp dẫn , đặc biệt là vì nó cần thêm dấu ngoặc đơn, nhưng không có nhiều lựa chọn thay thế.

Cuối cùng, tôi đã đôi khi thấy quy ước:

ScopedLock(myMutex), protectedFunction(); 

, mà tránh được việc phải phát minh ra một tên cho ScopedLock. Để nói với sự thật, tôi không thích nó, nhưng tôi đã thấy nó được sử dụng, và thay thế của việc thêm dấu ngoặc phụ để đảm bảo rằng ScopedLock là ngay lập tức phá hủy không phải là rất đẹp, hoặc.

+6

"* Việc sử dụng chính của toán tử dấu phẩy là obfuscation *" - Tôi không nghĩ đó là sự thật. Chắc chắn * có thể * được sử dụng theo cách đó, nhưng có rất nhiều hợp pháp (Nếu bạn giới hạn các quan sát của mình về mã được viết bởi người mới bắt đầu, có lẽ bạn đang đúng.) –

+0

@KeithThompson Cách sử dụng chính mà tôi đã thấy là sự xáo trộn. Tôi đưa ra một vài ví dụ về việc sử dụng nó có thể được biện minh, Tuy nhiên, nơi các lựa chọn thay thế không thực sự rõ ràng hơn việc sử dụng toán tử dấu phẩy. nó đã bị lạm dụng nhiều, và hầu hết các ví dụ được đăng trong các ví dụ khác là lạm dụng. (Thật thú vị, nó thường xuyên bị lạm dụng trong C++ hơn trong C. Trong C++, bạn có thể quá tải nó, và _all_ của việc sử dụng tôi đã nhìn thấy quá tải là lạm dụng.) –

4

này có thể được hiểu tốt hơn bằng cách lấy một số ví dụ:

Đầu tiên: Hãy xem xét một biểu:

x = ++j; 

Nhưng đối với thời điểm hiện tại, nếu chúng ta cần phải gán một giá trị debug tạm thời, sau đó chúng tôi có thể viết.

x = DEBUG_VALUE, ++j; 

Thứ hai:
Comma , nhà khai thác thường được sử dụng trong for() -loop ví dụ:

for(i = 0, j = 10; i < N; j--, i++) 
// ^    ^ here we can't use ; 

Thứ ba:
Thêm một ví dụ (trên thực tế người ta có thể tìm thấy làm thú vị này) :

if (x = 16/4), if remainder is zero then print x = x - 1; 
if (x = 16/5), if remainder is zero then print x = x + 1; 

Nó cũng có thể được thực hiện trong một bước;

if(x = n/d, n % d) // == x = n/d; if(n % d) 
    printf("Remainder not zero, x + 1 = %d", (x + 1)); 
    else 
    printf("Remainder is zero, x - 1 = %d", (x - 1)); 

PS: Nó cũng có thể thú vị khi biết rằng đôi khi nó là thảm họa để sử dụng , điều hành.Ví dụ trong câu hỏi Strtok usage, code not working, do nhầm lẫn, OP quên viết tên hàm và thay vì viết tokens = strtok(NULL, ",'");, anh viết tokens = (NULL, ",'"); và anh ta không nhận được lỗi biên dịch - nhưng biểu thức hợp lệ của nó là tokens = ",'"; gây ra một vòng lặp vô hạn trong chương trình của anh ta .

+0

@Sakham Cảm ơn Sakham! –

+2

Tôi nghĩ rằng ví dụ thứ 2 của bạn ('for') chiếm 99% toán tử dấu phẩy hợp pháp sử dụng. – ugoren

+0

@ugoren Có. sử dụng khác của ',' chỉ là loại obfuscation ví dụ 3rr. –

1

Dường như có ít sử dụng thực tế toán tử,().

Bjarne Stroustrup, Design và Sự phát triển của C++

Hầu hết các sử dụng thường được các dấu phẩy có thể được phát hiện ra trong bài viết wikipedia Comma_operator#Uses.

Một sử dụng thú vị tôi đã phát hiện ra khi sử dụng boost::assign, nơi mà nó đã sáng suốt quá tải toán tử để làm cho nó hoạt động như một dấu phẩy tách ra danh sách các giá trị mà có thể được đẩy đến cuối của một đối tượng vector

#include <boost/assign/std/vector.hpp> // for 'operator+=()' 
using namespace std; 
using namespace boost::assign; // bring 'operator+=()' into scope 

{ 
    vector<int> values; 
    values += 1,2,3,4,5,6,7,8,9; // insert values at the end of the container 
} 

Thật không may, việc sử dụng trên đó là phổ biến cho prototyping bây giờ sẽ trông cổ xưa khi trình biên dịch bắt đầu hỗ trợ Uniform Initialization

vì vậy mà lá chúng tôi trở lại

Dường như có ít sử dụng thực tế toán tử,().

Bjarne Stroustrup, Design và Sự phát triển của C++

0

Các boost::assign quá tải toán tử phẩy nặng nề để đạt loại cú pháp:

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9; 
0

Nó có thể là hữu ích cho việc điều hành hành trình nếu bạn muốn thực hiện hai hoặc nhiều hướng dẫn khi điều kiện là đúng hoặc false. nhưng hãy nhớ rằng giá trị trở sẽ là biểu hiện đúng nhất do các nhà điều hành dấu phẩy trái sang cai trị evalutaion phải (Ý tôi là bên trong dấu ngoặc đơn)

Ví dụ:

a<b?(x=5,b=6,d=i):exit(1);