Mengapa membuang nilai pengembalian yang tidak terpakai ke batal?


112
int fn();

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

Apakah ada alasan untuk membuang nilai pengembalian yang tidak terpakai, atau apakah saya benar berpikir itu hanya membuang-buang waktu?

Mengikuti:

Sepertinya itu cukup komprehensif. Saya kira itu lebih baik daripada mengomentari nilai kembali yang tidak digunakan karena kode yang mendokumentasikan diri lebih baik daripada komentar. Secara pribadi, saya akan mematikan peringatan ini karena itu suara yang tidak perlu.

Aku akan makan kata-kataku jika serangga lolos karenanya ...

Jawaban:


79

Jawaban David cukup banyak mencakup motivasi untuk ini, untuk secara eksplisit menunjukkan kepada "pengembang" lain bahwa Anda tahu fungsi ini kembali tetapi Anda secara eksplisit mengabaikannya.

Ini adalah cara untuk memastikan bahwa kode kesalahan yang diperlukan selalu ditangani.

Saya pikir untuk C ++ ini mungkin satu-satunya tempat yang saya lebih suka menggunakan cast gaya-C juga, karena menggunakan notasi cast statis penuh terasa seperti berlebihan di sini. Terakhir, jika Anda meninjau standar pengkodean atau menulisnya, ada baiknya juga untuk menyatakan secara eksplisit bahwa panggilan ke operator yang kelebihan beban (tidak menggunakan notasi panggilan fungsi) harus dikecualikan dari ini juga:

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.

ini adalah satu-satunya tempat saya lebih suka cast gaya-c juga. tho dalam standar pengkodean saya, saya akan menambahkan (void) ke "a + a;" juga (dikupas dengan benar, tentu saja: p)
Johannes Schaub - litb

8
Agak di luar topik, tapi mengapa Anda melakukan "a + a;" seperti itu tanpa menggunakan nilai kembali? Itu benar-benar tampak seperti penyalahgunaan efek samping bagi saya, dan mengaburkan maksud kode.
Rob K

5
Nah, yang akan Anda gunakan setiap hari adalah 'a = a'. Ini mengembalikan yang ditugaskan ke objek (untuk semua kelas berperilaku baik itu! :)). Contoh lainnya adalah: os << "Hello World" << std :: endl. Masing-masing mengembalikan objek "os".
Richard Corden

46

Di tempat kerja kami menggunakannya untuk mengetahui bahwa fungsi tersebut memiliki nilai kembali tetapi pengembang telah menegaskan bahwa aman untuk mengabaikannya. Karena Anda menandai pertanyaan sebagai C ++, Anda harus menggunakan static_cast :

static_cast<void>(fn());

Sejauh kompiler pergi mentransmisikan nilai kembali ke void memiliki sedikit arti.


Apakah itu menekan peringatan tentang nilai kembali yang tidak digunakan?
Paul Tomblin

Tidak. @Mykola: Dengan GCC4 Anda dapat melampirkan atribut yang mengatakan bahwa nilai yang dikembalikan tidak boleh diabaikan yang akan memicu peringatan.
David Holm

2
Saya menggunakan g ++ dan memberikan peringatan bahkan dengan cast seperti itu.
klew

2
opsi peringatan mana yang Anda gunakan? -Nilai yang tidak digunakan tidak memicu peringatan di lingkungan saya
Mykola Golubyev

Di VC ++, ini menekan peringatan
jalf

39

Alasan sebenarnya untuk melakukan ini berasal dari alat yang digunakan pada kode C, yang disebut lint .

Ini menganalisis kode untuk mencari kemungkinan masalah dan mengeluarkan peringatan dan saran. Jika suatu fungsi mengembalikan nilai yang kemudian tidak diperiksa, lintakan memperingatkan jika ini tidak disengaja. Untuk menonaktifkan lintperingatan ini, Anda melakukan panggilan ke (void).


2
Ini mungkin dimulai dengan cara ini, tetapi sebagian besar alat sekarang memiliki mekanisme lain untuk menekan peringatan seperti ini. Juga - terlepas dari mengapa ini dimulai, dalam domain kode kritis keamanan khususnya, ini telah menjadi cara yang biasa untuk "mendokumentasikan" maksud pengembang.
Richard Corden

5
GCC memberikan peringatan untuk ini dengan -Wall.
greyfade

23

Mentransmisikan ke voiddigunakan untuk menyembunyikan peringatan compiler untuk variabel yang tidak digunakan dan nilai atau ekspresi yang dikembalikan yang belum disimpan.

The Standard (2003) mengatakan dalam §5.2.9 / 4 mengatakan,

Ekspresi apa pun dapat secara eksplisit dikonversi ke jenis "cv void". Nilai ekspresi dibuang .

Jadi Anda bisa menulis:

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

Semua formulir valid. Saya biasanya membuatnya lebih pendek seperti:

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

Ini juga oke.


1
Kecuali itu tidak selalu mematikan peringatan kompiler. gcc sepertinya tidak menanggapi casting untuk batal
Matt

@Matt: Versi GCC mana yang Anda gunakan? Dapatkah Anda memposting kode Anda di ideone.com atau stacked-crooked.com (yang terakhir lebih baik).
Nawaz

1
Apakah kata-kata standar "dibuang" menyiratkan bahwa peringatan tidak boleh dinaikkan oleh linter?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

2
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功: .... itu adalah pertanyaan yang menarik. Saat ini, saya tidak tahu jawabannya. Tolong bagikan dengan saya jika Anda mengetahuinya.
Nawaz


4

Transmisi ke void tidak memerlukan biaya. Ini hanya informasi untuk kompilator bagaimana memperlakukannya.


1
Ini "tanpa biaya" jika waktu untuk menulisnya dan waktu berikutnya untuk membaca, memahami (dan kemudian mengabaikan) kode itu gratis. IMO, menulis (void)menghabiskan waktu dan energi saya dan membuat saya bertanya-tanya apakah penulis tahu bagaimana ekspresi bekerja.
wallyk

4

Untuk fungsionalitas program Anda, casting to void tidak ada artinya. Saya juga berpendapat bahwa Anda tidak boleh menggunakannya untuk memberi isyarat kepada orang yang membaca kode tersebut, seperti yang disarankan dalam jawaban David. Jika Anda ingin mengkomunikasikan sesuatu tentang niat Anda, lebih baik menggunakan komentar. Menambahkan pemeran seperti ini hanya akan terlihat aneh dan menimbulkan pertanyaan tentang kemungkinan alasannya. Hanya pendapat saya ...


2
Ini adalah pola yang diketahui untuk secara eksplisit memberi tahu orang lain bahwa Anda tidak peduli dengan nilai yang dikembalikan, bahwa Anda tidak lupa menanganinya.
Spidey

For the functionality of you program casting to void is meaninglessUntuk dokumentasi mandiri dan jumlah peringatan saat mengompilasi, sebenarnya tidak. you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.Komentar terbaik adalah tidak ada komentar , saat kode menjelaskan dirinya sendiri. Dan, sekali lagi, membuat compiler peringatan valid tetap senang dalam prosesnya.
underscore_d

'Menambahkan pemeran seperti ini hanya akan terlihat aneh dan menimbulkan pertanyaan tentang kemungkinan alasannya'. Saya menemukan salah satunya, dan itulah mengapa saya membaca ini. Tampak seperti penyalahgunaan tumpukan misterius bagi otak saya yang tidak terlatih.
mynameisnafe

1

Juga saat memverifikasi kode Anda sesuai dengan standar MISTA (atau lainnya), alat otomatis seperti LDRA tidak akan mengizinkan Anda memanggil fungsi yang memiliki tipe kembalian tanpa membuatnya mengembalikan nilai kecuali Anda secara eksplisit memasukkan nilai yang dikembalikan ke (void)


0

C ++ 17 [[nodiscard]]

C ++ 17 menstandarkan "bisnis yang diabaikan nilai pengembaliannya" dengan atribut.

Oleh karena itu, saya berharap penerapan yang sesuai akan selalu memberi peringatan hanya jika nodiscard diberikan, dan tidak pernah memperingatkan sebaliknya.

Contoh:

main.cpp

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

int main() {
    f();
}

menyusun:

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

hasil:

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() {
      | 

Berikut semua hindari peringatan:

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

Saya tidak dapat menggunakan maybe_unusedlangsung pada f()panggilan:

[[maybe_unused]] f();

memberikan:

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

Pekerjaan (void)pemeran tampaknya tidak wajib tetapi "didorong" dalam standar: Bagaimana saya bisa dengan sengaja membuang nilai kembalian [[nodiscard]]?

Juga terlihat dari pesan peringatan, salah satu "solusi" untuk peringatan tersebut adalah dengan menambahkan -Wno-unused-result:

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

meskipun saya tentu saja tidak akan merekomendasikan mengabaikan peringatan secara global seperti ini.

C ++ 20 juga memungkinkan Anda untuk menambahkan alasan untuk nodiscardseperti dalam [[nodiscard("reason")]]seperti yang disebutkan di: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

GCC warn_unused_resultAtribut

Sebelum standarisasi [[nodiscard]], dan untuk C sebelum mereka akhirnya memutuskan untuk menstandarkan atribut, GCC mengimplementasikan fungsi yang sama persis dengan warn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

pemberian yang mana:

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();
      |     ~^~

Perlu dicatat kemudian bahwa karena ANSI C tidak memiliki standar untuk ini, ANSI C tidak menentukan fungsi perpustakaan standar C mana yang memiliki atribut atau tidak dan oleh karena itu implementasi telah membuat keputusan sendiri tentang apa yang harus atau tidak ditandai dengan warn_unuesd_result, yang mana itulah sebabnya secara umum Anda harus menggunakan (void)cast untuk mengabaikan pengembalian panggilan apa pun ke fungsi library standar untuk sepenuhnya menghindari peringatan dalam implementasi apa pun.

Diuji di GCC 9.2.1, Ubuntu 19.10.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.