Tại sao chuyển các giá trị trả về không sử dụng thành void?


112
int fn();

void whatever()
{
    (void) fn();
}

Có lý do gì để chuyển một giá trị trả về chưa sử dụng thành vô hiệu không, hay tôi đã đúng khi nghĩ rằng đó là một sự lãng phí thời gian?

Theo sát:

Điều đó có vẻ khá toàn diện. Tôi cho rằng nó tốt hơn là bình luận một giá trị trả lại không sử dụng vì mã tự ghi lại tốt hơn bình luận. Cá nhân, tôi sẽ tắt những cảnh báo này vì đó là tiếng ồn không cần thiết.

Tôi sẽ ăn lời nếu một con bọ thoát ra vì nó ...

Câu trả lời:


79

Câu trả lời của David bao hàm khá nhiều động cơ cho việc này, để cho các "nhà phát triển" khác thấy rõ ràng rằng bạn biết hàm này trả về nhưng bạn đang bỏ qua nó một cách rõ ràng.

Đây là một cách để đảm bảo rằng các mã lỗi cần thiết luôn được xử lý.

Tôi nghĩ rằng đối với C ++, đây có lẽ là nơi duy nhất tôi thích sử dụng phôi kiểu C hơn, vì sử dụng ký hiệu truyền tĩnh đầy đủ chỉ cảm thấy như quá mức cần thiết ở đây. Cuối cùng, nếu bạn đang xem xét một tiêu chuẩn mã hóa hoặc viết một tiêu chuẩn mã hóa, thì bạn cũng nên tuyên bố rõ ràng rằng các lệnh gọi đến các toán tử quá tải (không sử dụng ký hiệu lệnh gọi hàm) cũng nên được miễn trừ điều này:

class A {};
A operator+(A const &, A const &);

int main () {
  A a;
  a + a;                 // Not a problem
  (void)operator+(a,a);  // Using function call notation - so add the cast.

đây là nơi duy nhất tôi thích diễn viên kiểu c hơn. tho trong tiêu chuẩn mã hóa của tôi, tôi sẽ thêm (void) vào "a + a;" quá (tất nhiên là được chăm chút đúng cách: p)
Johannes Schaub - litb

8
Hơi lạc đề, nhưng tại sao bạn lại làm "a + a;" như vậy mà không sử dụng giá trị trả về? Điều đó thực sự có vẻ như là một sự lạm dụng các tác dụng phụ đối với tôi và làm xáo trộn mục đích của mã.
Rob K

5
Chà, cái mà bạn sẽ sử dụng hàng ngày sẽ là 'a = a'. Điều này trả về đối tượng được gán cho (đối với tất cả các lớp hoạt động tốt! :)). Một ví dụ khác là: os << "Hello World" << std :: endl. Mỗi người trong số họ trả về đối tượng "os".
Richard Corden

46

Tại nơi làm việc, chúng tôi sử dụng điều đó để xác nhận rằng hàm có giá trị trả về nhưng nhà phát triển đã khẳng định rằng có thể an toàn khi bỏ qua nó. Vì bạn đã gắn thẻ câu hỏi là C ++, bạn nên sử dụng static_cast :

static_cast<void>(fn());

Theo như trình biên dịch đi thì việc truyền giá trị trả về thành void có rất ít ý nghĩa.


Nó có ngăn chặn cảnh báo về các giá trị trả về không sử dụng không?
Paul Tomblin

Không nó không. @Mykola: Với GCC4, bạn có thể đính kèm một thuộc tính cho biết rằng giá trị trả về không được bỏ qua, điều này sẽ kích hoạt cảnh báo.
David Holm

2
Tôi sử dụng g ++ và nó đưa ra cảnh báo ngay cả với dàn diễn viên như vậy.
klew

2
bạn sử dụng tùy chọn cảnh báo nào? -Wunused giá trị không kích hoạt cảnh báo trong môi trường của tôi
Mykola Golubyev

Trong VC ++, nó ngăn chặn cảnh báo
jalf

39

Lý do thực sự để làm điều này bắt nguồn từ một công cụ được sử dụng trên mã C, được gọi là lint .

Nó phân tích mã tìm kiếm các vấn đề có thể xảy ra và đưa ra các cảnh báo và đề xuất. Nếu một hàm trả về một giá trị mà sau đó không được kiểm tra, lintnó sẽ cảnh báo trong trường hợp điều này là ngẫu nhiên. Để tắt tiếng lintcảnh báo này, bạn thực hiện cuộc gọi tới (void).


2
Nó có thể đã bắt đầu theo cách này, nhưng hầu hết các công cụ hiện có các cơ chế khác để ngăn chặn các cảnh báo như thế này. Ngoài ra - bất kể lý do tại sao điều này bắt đầu, đặc biệt trong phạm vi của mã quan trọng an toàn, đây đã trở thành cách thông thường để "ghi lại" ý định của nhà phát triển.
Richard Corden

5
GCC đưa ra một cảnh báo cho điều này với -Wall.
greyfade

23

Truyền tới voidđược sử dụng để ngăn chặn cảnh báo trình biên dịch cho các biến không sử dụng và các giá trị hoặc biểu thức trả về chưa được lưu.

Tiêu chuẩn (2003) nói trong §5.2.9 / 4 nói,

Bất kỳ biểu thức nào cũng có thể được chuyển đổi rõ ràng thành kiểu “cv void”. Giá trị biểu thức bị loại bỏ .

Vì vậy, bạn có thể viết:

//suppressing unused variable warnings
static_cast<void>(unusedVar);
static_cast<const void>(unusedVar);
static_cast<volatile void>(unusedVar);

//suppressing return value warnings
static_cast<void>(fn());
static_cast<const void>(fn());
static_cast<volatile void>(fn());

//suppressing unsaved expressions
static_cast<void>(a + b * 10);
static_cast<const void>( x &&y || z);
static_cast<volatile void>( m | n + fn());

Tất cả các hình thức đều hợp lệ. Tôi thường làm cho nó ngắn hơn như sau:

//suppressing  expressions
(void)(unusedVar);
(void)(fn());
(void)(x &&y || z);

Nó cũng không sao.


1
Ngoại trừ việc nó không phải lúc nào cũng tắt các cảnh báo của trình biên dịch. gcc dường như không đáp ứng với đúc để trống
Matt

@Matt: Bạn đang sử dụng phiên bản GCC nào? Bạn có thể đăng mã của bạn tại ideone.com hoặc stacked-crooked.com (cái sau tốt hơn).
Nawaz

1
Liệu từ ngữ tiêu chuẩn "bị loại bỏ" có ngụ ý rằng cảnh báo không nên nêu ra bởi những người trồng vải không?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

2
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功: .... đó là một câu hỏi thú vị. Hiện tại, tôi không biết câu trả lời cho điều đó. Xin vui lòng chia sẻ với tôi nếu bạn biết nó dù sao.
Nawaz,

8

Kể từ c ++ 17, chúng ta có [[maybe_unused]]thuộc tính có thể được sử dụng thay vì voidép kiểu.


1
C ++ 17 cũng được thêm vào nodiscardmà có thể được quan tâm: stackoverflow.com/a/61675726/895245 Ngoài ra, tôi nghĩ rằng bạn không thể áp dụng [[maybe_unused]]trực tiếp cho cuộc gọi, dường như chỉ cho một biến giả chấp nhận trả về của cuộc gọi? Nếu điều đó là chính xác, nó là một chút khó khăn.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

4

Truyền đến vô hiệu là không tốn kém. Nó chỉ là thông tin cho trình biên dịch cách xử lý nó.


1
Sẽ là "miễn phí" nếu thời gian để viết nó và thời gian tiếp theo để đọc, hiểu (và sau đó bỏ qua) mã là miễn phí. IMO, việc viết lách (void)khiến tôi tốn nhiều thời gian và năng lượng và khiến tôi tự hỏi liệu tác giả có biết cách diễn đạt hoạt động hay không.
wallyk

4

Đối với chức năng của bạn, việc truyền chương trình thành vô hiệu là vô nghĩa. Tôi cũng tranh luận rằng bạn không nên sử dụng nó để báo hiệu điều gì đó cho người đang đọc mã, như gợi ý trong câu trả lời của David. Nếu bạn muốn thông báo điều gì đó về ý định của mình, tốt hơn là sử dụng một bình luận. Thêm một dàn diễn viên như thế này sẽ chỉ trông kỳ lạ và đặt ra câu hỏi về lý do có thể xảy ra. Chỉ là ý kiến ​​của tôi ...


2
Đây là một mẫu đã biết để nói rõ ràng với người khác rằng bạn không quan tâm đến giá trị trả về, rằng bạn đã không quên xử lý nó.
Spidey

For the functionality of you program casting to void is meaninglessĐối với tự tài liệu và số lượng cảnh báo khi biên dịch, nó thực sự là không. you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.Nhận xét tốt nhất là không có bình luận , khi mã giải thích chính nó. Và, một lần nữa, giữ cho một trình biên dịch cảnh báo hợp lệ luôn vui vẻ trong quá trình này.
underscore_d

'Thêm một dàn diễn viên như thế này sẽ chỉ trông kỳ lạ và đặt ra câu hỏi về lý do có thể xảy ra'. Tôi đã phát hiện ra một trong những điều này, và đó là lý do tại sao tôi đọc nó. Có vẻ như sự lạm dụng chồng chất bí ẩn đối với bộ não chưa được đào tạo của tôi.
mynameisnafe

1

Ngoài ra, khi xác minh mã của bạn tuân thủ các tiêu chuẩn MISTA (hoặc các tiêu chuẩn khác), các công cụ tự động như LDRA sẽ không cho phép bạn gọi một hàm có kiểu trả về mà không cần nó trả về giá trị trừ khi bạn truyền rõ ràng giá trị trả về thành (void)


0

C ++ 17 [[nodiscard]]

C ++ 17 đã chuẩn hóa "giá trị trả về doanh nghiệp bị bỏ qua" với một thuộc tính.

Do đó, tôi hy vọng rằng các triển khai tuân thủ sẽ luôn chỉ cảnh báo khi nodiscardđược đưa ra và không bao giờ cảnh báo khác.

Thí dụ:

main.cpp

[[nodiscard]] int f() {
    return 1;
}

int main() {
    f();
}

biên dịch:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -o main.out main.cpp

kết quả:

main.cpp: In function int main()’:
main.cpp:6:6: warning: ignoring return value of int f()’, declared with attribute nodiscard [-Wunused-result]
    6 |     f();
      |     ~^~
main.cpp:1:19: note: declared here
    1 | [[nodiscard]] int f() {
      | 

Tất cả những điều sau đây đều tránh được cảnh báo:

(void)f();
[[maybe_unused]] int i = f();

Tôi không thể sử dụng maybe_unusedtrực tiếp trong f()cuộc gọi:

[[maybe_unused]] f();

cho:

main.cpp: In function int main()’:
main.cpp:6:5: warning: attributes at the beginning of statement are ignored [-Wattributes]
    6 |     [[maybe_unused]] f();
      |     ^~~~~~~~~~~~~~~~

Việc ép kiểu(void) dường như không bắt buộc nhưng được "khuyến khích" trong tiêu chuẩn: Làm cách nào để tôi có thể cố ý loại bỏ giá trị trả về [[thẻ nút]]?

Cũng như đã thấy từ thông báo cảnh báo, một "giải pháp" cho cảnh báo là thêm -Wno-unused-result:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -Wno-unused-result -o main.out main.cpp

mặc dù tất nhiên tôi sẽ không khuyên bạn nên bỏ qua các cảnh báo trên toàn cầu như thế này.

C ++ 20 cũng cho phép bạn thêm một lý do để các nodiscardnhư trong [[nodiscard("reason")]]như đã đề cập tại địa chỉ: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

warn_unused_resultThuộc tính GCC

Trước khi tiêu chuẩn hóa [[nodiscard]]và đối với C trước khi cuối cùng họ quyết định tiêu chuẩn hóa các thuộc tính, GCC đã triển khai cùng chức năng chính xác với warn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

mang lại:

main.cpp: In function int main()’:
main.cpp:8:6: warning: ignoring return value of int f()’, declared with attribute warn_unused_result [-Wunused-result]
    8 |     f();
      |     ~^~

Cần lưu ý rằng vì ANSI C không có tiêu chuẩn cho điều này, ANSI C không chỉ định các hàm thư viện tiêu chuẩn C có thuộc tính nào hay không và do đó việc triển khai đã tự đưa ra quyết định về cái gì nên hay không được đánh dấu warn_unuesd_result, cái nào là lý do tại sao nói chung bạn sẽ phải sử dụng (void)ép kiểu để bỏ qua trả về của bất kỳ lệnh gọi nào đến các hàm thư viện tiêu chuẩn để tránh hoàn toàn các cảnh báo trong bất kỳ triển khai nào.

Đã thử nghiệm trong GCC 9.2.1, Ubuntu 19.10.

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.