Bagaimana cara terbaik membungkam peringatan tentang variabel yang tidak digunakan?

237

Saya memiliki aplikasi lintas platform dan dalam beberapa fungsi saya tidak semua nilai yang diteruskan ke fungsi digunakan. Karenanya saya mendapat peringatan dari GCC yang memberi tahu saya bahwa ada variabel yang tidak digunakan.

Apa cara pengkodean terbaik di sekitar peringatan?

# Jika ada di sekitar fungsi?

#ifdef _MSC_VER
void ProcessOps::sendToExternalApp(QString sAppName, QString sImagePath, qreal qrLeft, qreal qrTop, qreal qrWidth, qreal qrHeight)
#else
void ProcessOps::sendToExternalApp(QString sAppName, QString sImagePath, qreal /*qrLeft*/, qreal /*qrTop*/, qreal /*qrWidth*/, qreal /*qrHeight*/)
#endif
{

Ini sangat jelek tetapi sepertinya cara kompiler lebih suka.

Atau apakah saya menetapkan nol pada variabel di akhir fungsi? (Yang saya benci karena itu mengubah sesuatu dalam aliran program untuk membungkam peringatan kompiler).

Apakah ada cara yang benar?

Phil Hannent
sumber
7
Saya baru sadar Anda mengajukan pertanyaan serupa November lalu. Inilah mengapa itu terlihat akrab! ;) stackoverflow.com/questions/308277/…
Alex B
9
Mengapa tidak berkomentar saja untuk kedua kompiler? Jika arg tidak terpakai pada satu, itu mungkin tidak akan digunakan pada yang lain ...
Roger Lipscombe
12
Anda harus tahu bahwa Qt memiliki Q_UNUSEDmakro hanya untuk ini. Lihat di dokumentasi.
Evan Teran
1
Solusi C berfungsi dengan baik di C ++ juga: stackoverflow.com/a/3599170/1904815
JonnyJD
-Tidak-tidak-digunakan-parameter juga bisa menjadi pilihan jika Anda dapat memiliki flag build spesifik-kompiler
Code Abominator

Jawaban:

327

Anda bisa meletakkannya dalam (void)var;ekspresi " " (tidak melakukan apa - apa) sehingga kompiler melihatnya digunakan. Ini portabel antar kompiler.

Misalnya

void foo(int param1, int param2)
{
    (void)param2;
    bar(param1);
}

Atau,

#define UNUSED(expr) do { (void)(expr); } while (0)
...

void foo(int param1, int param2)
{
    UNUSED(param2);
    bar(param1);
}
Alex B
sumber
22
+1 - tetap saya akan mendokumentasikan mengapa Anda tidak menggunakan variabel meskipun itu ada.
Tobias Langner
18
Ini adalah bagaimana Q_UNUSEDdiimplementasikan pada prinsipnya.
Dmitry Volosnykh
11
@Cameron Anda cukup menghilangkan nama parameter di C ++. Jika templated, itu tidak akan digunakan dalam C, jadi Anda tidak perlu trik cast-to-void.
Alex B
13
Hanya #define UNUSED(expr) (void)(expr)harus bekerja terlalu (tanpa do-while).
JonnyJD
7
Saya bertanya-tanya bagaimana cara melakukannya untuk template variadic. Di template<typename... Args> void f(const Args&... args)saya tidak bisa menulis (void)args;atau (void)args...;karena keduanya kesalahan sintaks.
panzi
101

Di GCC dan Dentang Anda dapat menggunakan __attribute__((unused))arahan preprosesor untuk mencapai tujuan Anda.
Sebagai contoh:

int foo (__attribute__((unused)) int bar) {
   return 0;
}
ezpz
sumber
1
Ini adalah solusi terbaik untuk fungsi panggilan balik.
Sonic Atom
1
Juga didukung oleh dentang: clang.llvm.org/docs/…
Alexander
39

Solusi Anda saat ini adalah yang terbaik - beri komentar nama parameter jika Anda tidak menggunakannya. Itu berlaku untuk semua kompiler, jadi Anda tidak perlu menggunakan pra-prosesor untuk melakukannya secara khusus untuk GCC.

alex tingle
sumber
7
Hanya untuk memperkuat jawaban ini - Anda tidak perlu #ifdef, cukup beri komentar pada nama parameter yang tidak digunakan.
quamrana
4
Saya punya kasus di mana parameter merupakan bagian dari panggilan balik dan mengomentari itu memecah kompilasi (jadi saya tidak yakin mengapa g++peringatan tentang hal itu.) Dalam kasus seperti itu, apa yang akan Anda rekomendasikan?
Drew Noakes
1
Bayangkan metode virtual sebaris dengan parameter yang tidak digunakan / * berkomentar * /, klien antarmuka tidak akan melihat nama parameter selama pelengkapan otomatis di sebagian besar IDE. Dalam hal ini solusi UNUSED () lebih nyaman, meskipun lebih bersih.
cbuchart
Saya pikir lebih sederhana lebih baik, berkomentar sangat jelas
fievel
26

Pembaruan C ++ 17

Dalam C ++ 17 kita mendapatkan atribut [[mungkin_unused]] yang tercakup dalam [dcl.attr.unused]

Atribut-token maybe_unused menunjukkan bahwa nama atau entitas mungkin sengaja tidak digunakan. Itu akan muncul paling banyak sekali dalam setiap daftar atribut dan tidak ada atribut-argumen-klausa akan hadir. ...

Contoh:

 [[maybe_unused]] void f([[maybe_unused]] bool thing1,
                        [[maybe_unused]] bool thing2) {
  [[maybe_unused]] bool b = thing1 && thing2;
    assert(b);
 }

Implementasi tidak boleh memperingatkan bahwa b tidak digunakan, baik NDEBUG didefinisikan atau tidak. —Kirim contoh]

Untuk contoh berikut:

int foo ( int bar) {
    bool unused_bool ;
    return 0;
}

Baik dentang dan gcc menghasilkan diagnostik menggunakan -Wall -Wextra untuk bilah dan unused_bool ( Lihat langsung ).

Saat menambahkan [[mungkin_tidak digunakan]] membungkam diagnostik:

int foo ([[maybe_unused]] int bar) {
    [[maybe_unused]] bool unused_bool ;
    return 0;
}

lihat langsung .

Sebelum C ++ 17

Dalam C ++ 11 bentuk UNUSEDmakro alternatif dapat dibentuk menggunakan ekspresi lambda ( melalui Ben Deane ) dengan menangkap variabel yang tidak digunakan:

#define UNUSED(x) [&x]{}()

Doa langsung dari ungkapan lambda harus dioptimalkan, dengan memberikan contoh berikut:

int foo (int bar) {
    UNUSED(bar) ;
    return 0;
}

kita dapat melihat di godbolt bahwa panggilan dioptimalkan jauh:

foo(int):
xorl    %eax, %eax
ret
Shafik Yaghmour
sumber
5
Jadi Anda menyebutkan C ++ 11 dan kemudian berhasil menyajikan makro ?! Aduh! Mungkin menggunakan fungsi akan lebih bersih? template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); }Anda juga bisa menggunakan lambda dalam fungsinya, saya kira.
Alexis Wilke
godbolt adalah sumber yang bagus
yano
5
[&x]{}()tidak benar-benar membungkam peringatan, tetapi meneruskan peringatan dari fungsi pemanggil ke lambda sebagai gantinya. Diperlukan waktu hingga kompiler mengidentifikasi ini sebagai peringatan, tetapi clang-rapi sudah mengeluhkan variabel yang tidak digunakan dalam daftar tangkap.
nVxx
25

Cara yang lebih bersih adalah dengan mengomentari hanya nama variabel:

int main(int /* argc */, char const** /* argv */) {
  return 0;
}
Marcin Wyszynski
sumber
8
Ini tidak baik jika Anda memiliki oksigen dan ingin mendokumentasikan parameter.
Alexis Wilke
18
@AlexisWilke: Itu akan memenuhi syarat sebagai bug di oksigen, IMO
6502
3
Anda dapat #mendefinisikan YOUR_PROJECT_UNUSED (argname) secara kondisional pada #ifdef DOXYGEN sehingga doxygen dapat melihat nama dan kompiler yang sebenarnya tidak, melalui int main (int YOUR_PROJECT_UNUSED (argc), ...). Tidak luar biasa, tetapi bekerja.
mabraham
Saya merasa sangat menyakitkan untuk mengomentari satu blok kode dengan banyak komentar bersarang. (kompiler mengeluh tentang setiap orang).
Jeff McClintock
@JeffMcClintock cukup gunakan komentar single-line. Kebanyakan editor yang layak mendukung pengeditan blok vertikal (mis. [Ctrl] + [V] di Vim). Kalau tidak, gunakan #if 0 / #endifblokir komentar.
Ruslan
24

Seorang rekan kerja hanya menunjuk saya ke makro kecil yang menyenangkan di sini

Untuk memudahkan saya akan menyertakan makro di bawah ini.

#ifdef UNUSED
#elif defined(__GNUC__) 
# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) 
#elif defined(__LCLINT__) 
# define UNUSED(x) /*@unused@*/ x 
#else 
# define UNUSED(x) x 
#endif

void dcc_mon_siginfo_handler(int UNUSED(whatsig))

sumber
12
"nice" "macro" "c ++" - pick 2.
Jeff McClintock
23

tidak menandai peringatan ini secara default. Peringatan ini harus dinyalakan baik secara eksplisit dengan meneruskan -Wunused-parameterke kompiler atau secara implisit dengan melewati -Wall -Wextra(atau mungkin beberapa kombinasi bendera lainnya).

Peringatan parameter yang tidak digunakan dapat dengan mudah ditekan dengan melewati -Wno-unused-parametercompiler, tetapi perhatikan bahwa flag penonaktifan ini harus datang setelah setiap kemungkinan flag yang memungkinkan untuk peringatan ini di baris perintah compiler, sehingga dapat berlaku.

Trauma Digital
sumber
2
Meskipun, ini mungkin bukan jawaban terbaik untuk pertanyaan (karena pertanyaannya adalah bagaimana menghindari peringatan, bukan cara menonaktifkannya), jawaban ini mungkin orang-orang yang berasal dari google (seperti saya) sedang mencari ("bagaimana untuk menonaktifkan peringatan ini "). Jadi saya beri +1, terima kasih atas jawaban Anda!
mozzbozz
13

cara makro-kurang dan portabel untuk mendeklarasikan satu atau lebih parameter sebagai tidak terpakai:

template <typename... Args> inline void unused(Args&&...) {}

int main(int argc, char* argv[])
{
    unused(argc, argv);
    return 0;
}
Philippe
sumber
Sangat bagus, tetapi perhatikan bahwa ini membutuhkan C ++ 11 (atau lebih baru, tentu saja).
Paul R
Saya memilih jawaban ini karena saya tidak ingin mengorbankan waktu kompilasi (dengan menggunakan templat) hanya untuk menghilangkan peringatan.
Konrad Kleine
@KonradKleine: Berapa banyak waktu kompilasi yang bisa dikonsumsi? Menguji di komputer saya, saya bisa menjalankan seribu panggilan () yang tidak digunakan ini dalam sepersepuluh detik.
Daniel McLaury
@DanielMcLaury ini hanya dugaan saya dan saya belum melakukan percobaan.
Konrad Kleine
8

Menggunakan arahan preprosesor dianggap jahat sebagian besar waktu. Idealnya Anda ingin menghindari mereka seperti Hama. Ingatlah bahwa membuat kompiler memahami kode Anda itu mudah, membiarkan programmer lain memahami kode Anda jauh lebih sulit. Beberapa lusin kasus seperti ini di sana-sini membuatnya sangat sulit untuk dibaca sendiri nanti atau untuk orang lain sekarang.

Salah satu cara mungkin untuk menempatkan parameter Anda bersama-sama menjadi semacam kelas argumen. Anda kemudian dapat menggunakan hanya sebagian dari variabel (setara dengan Anda menetapkan 0 benar-benar) atau memiliki spesialisasi yang berbeda dari kelas argumen untuk setiap platform. Namun ini mungkin tidak sepadan, Anda perlu menganalisis apakah itu cocok.

Jika Anda dapat membaca templat yang tidak mungkin, Anda mungkin menemukan tips lanjutan di buku "Exceptional C ++". Jika orang-orang yang akan membaca kode Anda dapat memperoleh keahlian mereka untuk mencakup hal-hal gila yang diajarkan dalam buku itu, maka Anda akan memiliki kode yang indah yang juga dapat dengan mudah dibaca. Kompiler juga akan sangat menyadari apa yang Anda lakukan (alih-alih menyembunyikan semuanya dengan preprocessing)

Ben Dadsetan
sumber
5
"Menggunakan arahan preprosesor dianggap jahat sebagian besar waktu." Betulkah? Oleh siapa
Graeme Perrow
12
Oleh siapa pun yang peduli tentang ruang lingkup, mampu men-debug dengan benar, atau kewarasan mereka.
Bill
2
@Graeme, kelihatannya tidak bersalah ketika kita hanya melihat 4 baris, tetapi menyebar di sekitarnya tidak menyebabkan sakit kepala. #ifdef pada dasarnya memungkinkan Anda untuk meletakkan beberapa versi kode sumber yang hanya akan dilihat oleh kompiler. Seperti yang disebutkan oleh Bill, ini juga mempersulit proses debug. Saya telah membaca tentang kejahatan arahan preprosesor di berbagai buku dan blog, serta pernah mengalaminya sendiri. Tentu saja, semuanya itu relatif. Kadang-kadang arahan preprosesor cukup masuk akal karena hal lain akan memiliki konsekuensi yang lebih buruk, dan poin saya di sini hanya bahwa itu harus dihindari jika memungkinkan.
Ben Dadsetan
1
Terlalu sering itu buruk, tapi saya akan menyebutnya #define UNUSED(expr) (void)(expr)pantas.
JonnyJD
7

Peringatan pertama dihasilkan oleh definisi variabel dalam file sumber bukan file header. Header dapat tetap asli dan harus, karena Anda mungkin menggunakan sesuatu seperti doxygen untuk menghasilkan dokumentasi API.

Saya akan berasumsi bahwa Anda memiliki implementasi yang sama sekali berbeda dalam file sumber. Dalam kasus ini, Anda dapat mengomentari parameter yang menyinggung atau hanya menulis parameter.

Contoh:

func(int a, int b)
{
    b;
    foo(a);
}

Ini mungkin tampak samar, sehingga mendefinisikan makro seperti TIDAK DIGUNAKAN. Cara MFC melakukannya adalah:

#ifdef _DEBUG
#define UNUSED(x)
#else
#define UNUSED(x) x
#endif

Seperti ini Anda melihat peringatan masih dalam debug membangun, mungkin bisa membantu.

rioki
sumber
4

Apakah tidak aman untuk selalu mengomentari nama parameter? Jika tidak, Anda dapat melakukan sesuatu seperti

#ifdef _MSC_VER
# define P_(n) n
#else
# define P_(n)
#endif

void ProcessOps::sendToExternalApp(
    QString sAppName, QString sImagePath,
    qreal P_(qrLeft), qreal P_(qrTop), qreal P_(qrWidth), qreal P_(qrHeight))

Ini sedikit lebih jelek.

Michael Krelin - hacker
sumber
4
Fakta bahwa nama param tidak wajib dalam C ++ - itu dalam C - hanya untuk memberikan cara standar dan mudah untuk mencegah peringatan.
Pemrogram
1
@ pemburu, tidak pernah mengatakan itu. Saya cenderung menunjukkan perbedaan antara C dan C ++, terutama ketika mereka berada di daerah yang Anda pikir adalah subset umum ... Hanya kebiasaan karena saya sedang bekerja pada basis kode campuran.
Pemrogram
4

Saya telah melihat ini alih-alih (void)param2cara membungkam peringatan:

void foo(int param1, int param2)
{
    std::ignore = param2;
    bar(param1);
}

Sepertinya ini ditambahkan di C ++ 11

krupan
sumber
Tampaknya melakukan sesuatu, tidak diabaikan setelah dikompilasi.
GyuHyeon Choi
3

Menggunakan UNREFERENCED_PARAMETER(p)bisa berhasil. Saya tahu itu didefinisikan dalam WinNT.h untuk sistem Windows dan dapat dengan mudah didefinisikan untuk gcc juga (jika belum memilikinya).

UNREFERENCED PARAMETER(p) didefinisikan sebagai

#define UNREFERENCED_PARAMETER(P)          (P)

di WinNT.h.

Joshua
sumber
2

Gunakan flag compiler, mis. Flag untuk GCC: -Wno-unused-variable

stacker
sumber
1

Anda bisa menggunakan __unuseduntuk memberi tahu kompiler bahwa variabel mungkin tidak digunakan.

- (void)myMethod:(__unused NSObject *)theObject    
{
    // there will be no warning about `theObject`, because you wrote `__unused`

    __unused int theInt = 0;
    // there will be no warning, but you are still able to use `theInt` in the future
}
OlDor
sumber
2
Kompiler yang mana ? Karena __unusedbukan standar C ++, dan lebih tepatnya, Anda juga tidak memposting ... Itu Objective-C. Jadi jawaban ini hanya benar-benar berguna untuk kompiler tertentu, dan itu membuat kode non-portable, dan pada kenyataannya tidak benar-benar valid karena kode pengguna tidak dimaksudkan untuk menggunakan pengidentifikasi yang diawali dengan __, yang disediakan untuk implementasi.
underscore_d
1

Di C ++ 11, ini adalah solusi yang saya gunakan:

template<typename... Ts> inline void Unreferenced(Ts&&...) {}

int Foo(int bar) 
{
    Unreferenced(bar);
    return 0;
}

int Foo2(int bar1, int bar2) 
{
    Unreferenced(bar1, bar2);
    return 0;
}

Diverifikasi sebagai perangkat portabel (setidaknya pada msvc modern, dentang dan gcc) dan tidak menghasilkan kode tambahan saat optimasi diaktifkan. Tanpa optimasi, pemanggilan fungsi ekstra dilakukan dan referensi ke parameter disalin ke stack, tetapi tidak ada makro yang terlibat.

Jika kode tambahan adalah masalah, Anda dapat menggunakan deklarasi ini sebagai gantinya:

(decltype(Unreferenced(bar1, bar2)))0;

tetapi pada saat itu, makro menyediakan keterbacaan yang lebih baik:

#define UNREFERENCED(...) { (decltype(Unreferenced(__VA_ARGS__)))0; }
Nampo
sumber
1

Ini berfungsi dengan baik tetapi membutuhkan C ++ 11

template <typename ...Args>
void unused(Args&& ...args)
{
  (void)(sizeof...(args));
}
Gian Lorenzo Meocci
sumber
1
Bagaimana dengan ini membutuhkan C ++ 14 dan tidak akan bekerja di C ++ 11? Saya tidak bisa melihat apa-apa. Juga, tidak dianjurkan untuk menggunakan ALLCAPSapa pun kecuali makro, yang membuat mereka terlihat jelek dan tidak diinginkan, tetapi tidak ada hal buruk tentang ini, sungguh, kecuali bahwa static_castakan lebih baik.
underscore_d
0

Saya menemukan sebagian besar jawaban yang disajikan hanya berfungsi untuk variabel lokal yang tidak digunakan, dan akan menyebabkan kesalahan kompilasi untuk variabel global statis yang tidak digunakan.

Makro lain diperlukan untuk menekan peringatan variabel global statis yang tidak digunakan.

template <typename T>
const T* UNUSED_VARIABLE(const T& dummy) { 
    return &dummy;
}
#define UNUSED_GLOBAL_VARIABLE(x) namespace {\
    const auto dummy = UNUSED_VARIABLE(x);\
}

static int a = 0;
UNUSED_GLOBAL_VARIABLE(a);

int main ()
{
    int b = 3;
    UNUSED_VARIABLE(b);
    return 0;
}

Ini berfungsi karena tidak ada peringatan yang akan dilaporkan untuk variabel global non-statis di ruang nama anonim.

Diperlukan C ++ 11

 g++  -Wall -O3  -std=c++11 test.cpp
FaceBro
sumber
0

Lol! Saya tidak berpikir ada pertanyaan lain pada SO yang mengungkapkan semua bidat yang dirusak oleh Chaos lebih baik dari yang ini!

Dengan segala hormat terhadap C ++ 17 ada pedoman yang jelas dalam Pedoman Inti C ++ . AFAIR, kembali pada tahun 2009 opsi ini tersedia serta hari ini. Dan jika seseorang mengatakan itu dianggap sebagai bug di Doxygen maka ada bug di Doxygen

kreuzerkrieg
sumber
-14

Saya tidak melihat masalah Anda dengan peringatan itu. Dokumentasikan dalam header metode / fungsi yang dikompilasi xy akan mengeluarkan peringatan (benar) di sini, tetapi variabel tesis ini diperlukan untuk platform z.

Peringatan sudah benar, tidak perlu mematikannya. Itu tidak membatalkan program - tetapi harus didokumentasikan, bahwa ada alasannya.

Tobias Langner
sumber
20
Masalahnya adalah, jika Anda memiliki ratusan atau ribuan peringatan seperti itu, Anda mungkin kehilangan salah satu yang berguna. (Dua kali saya berada dalam situasi untuk mengarungi beberapa ribu peringatan, menghilangkan sebagian besar, dan menemukan beberapa benar-benar berguna sekali yang mengisyaratkan kesalahan serius.) Itu selalu baik untuk dikompilasi tanpa peringatan, jika mungkin pada tingkat peringatan tertinggi.
sbi
4
Dalam sebuah proyek yang saya kerjakan tahun lalu saya menyalakan level peringatan tertinggi dan mendapat ~ 10.000 peringatan. Hanya beberapa lusin yang benar-benar membantu. Di antara mereka yang tersembunyi sekitar selusin bug yang benar-benar jahat, tetapi butuh beberapa minggu orang untuk membersihkan basis kode ke titik di mana orang benar-benar bisa melihat beberapa yang serius. Seandainya tingkat peringatan naik terus dan basis kode tetap bebas peringatan, kesalahan itu tidak akan pernah masuk ke dalam kode.
sbi
1
maaf - tetapi melakukan analisis kode statis (menggunakan alat apa pun yang Anda miliki, bahkan jika itu hanya kompiler) di akhir proyek ini sedikit seperti pemrograman seluruh program dan ketika Anda selesai, tekan kompilasi dan berharap Anda tidak memiliki kesalahan.
Tobias Langner
2
@ Richard: Saya mengerjakan proyek dengan ribuan file sumber. Sebuah peringatan kecil di sana-sini, bahkan yang terdokumentasi dengan baik, dengan cepat bertambah. Sekalipun Anda hanya memiliki lusinan peringatan yang berkedip saat membangun (alih-alih ratusan atau ribuan), harus mencarinya secara terpisah untuk melihat apakah peringatan itu baru atau terdokumentasi terlalu memakan waktu dan, pada akhirnya, tidak akan t dilakukan. Oleh karena itu: Kompilasi pada tingkat peringatan setinggi mungkin dengan nol peringatan. Setiap peringatan yang muncul akan segera diperhatikan, dilihat, dan diperbaiki atau ditanggapi.
sbi
2
@ sbi: turining pada tingkat peringatan tertinggi untuk kompiler Anda adalah beberapa bentuk analisis kode statis. Analisis kode statis hanya membaca kode tanpa menjalankannya dan mengurangi informasi darinya. Itulah yang dilakukan kompiler ketika dia memeriksa peraturannya untuk peringatan.
Tobias Langner