Tham số không được sử dụng trong c ++ 11


79

Trong c ++ 03 trở về trước để tắt cảnh báo trình biên dịch về tham số không sử dụng, tôi thường sử dụng mã như vậy:

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

Ví dụ

int main(int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);

    return 0;
}

Nhưng macro không phải là phương pháp hay nhất cho c ++, vì vậy. Có giải pháp nào tốt hơn xuất hiện với tiêu chuẩn c ++ 11 không? Ý tôi là tôi có thể loại bỏ macro không?

Cảm ơn vì tất cả!


11
Chắc chắn rồi. Tắt cảnh báo.
Pete Becker

64
Không! Đừng làm thế!
Lightness Races in Orbit

9
Macro đó tốt hơn bao nhiêu so với việc mở rộng nó trong dòng? (void)argc;là ngắn hơn và rõ ràng hơnUNUSED(argc);
David Rodríguez - dribeas

22
Tôi thích unused(argc, argv)với template<class... T> void unused(T&&...){}. Rõ ràng, ngắn gọn và không có macro.
Xeo

19
@MadScientist nhưng bạn có thể để lại đối số không tên, hoặc thậm chí chỉ bình luận về tên của nó. void foo(int /*unused_arg*/, int used_arg)
kassak

Câu trả lời:


40

Tôi đã sử dụng một hàm với phần thân trống cho mục đích đó:

template <typename T>
void ignore(T &&)
{ }

void f(int a, int b)
{
  ignore(a);
  ignore(b);
  return;
}

Tôi mong đợi bất kỳ trình biên dịch nghiêm túc nào sẽ tối ưu hóa lệnh gọi hàm và nó làm im lặng các cảnh báo cho tôi.


20
Khi nào Tlà một tham số mẫu, T&&là một tham chiếu chung liên kết với bất kỳ thứ gì.
Angew không còn tự hào về SO

4
+1 Mặc dù phiên bản nâng cao của Xeo từ nhận xét của anh ấy thậm chí không được đề cập đến.
Christian Rau

12
Tại sao lại bỏ qua phương thức tích hợp sẵn? Đơn giản chỉ cần bỏ qua tên tham số.
Jack Aidley

26
-1, điều này là vô lý và là một điều không cần thiết, đặc biệt là khi bạn chỉ có thể bỏ qua tên tham số. Thành thật mà nói, tôi thấy phiền rằng bằng cách nào đó điều này có 25 lượt ủng hộ.
TC1

5
@ TC1 Điều này làm cho mã của bạn rõ ràng về những gì nó làm và tại sao. Có các tham số hoặc biến không được sử dụng là một mùi trong mã của bạn và điều này làm cho nó rõ ràng. Tắt cảnh báo làm cho mã của bạn có mùi hơn.
dascandy

203

Bạn chỉ có thể bỏ qua tên tham số:

int main(int, char *[])
{

    return 0;
}

Và trong trường hợp của main, bạn thậm chí có thể bỏ qua các thông số hoàn toàn:

int main()
{
    // no return implies return 0;
}

Xem "§ 3.6 Bắt đầu và Kết thúc" trong Tiêu chuẩn C ++ 11.


12
Và trong trường hợp của main, bạn có thể bỏ qua các tham số hoàn toàn. Và returntuyên bố, cho vấn đề đó.
Mike Seymour

4
@MikeSeymour Tôi thực sự coi việc bỏ qua câu lệnh return là một phương pháp hay.
jtepe

6
@jotep Được rồi, tôi sẽ cắn. Tại sao bạn lại coi đó là thực hành tốt?
Peter Wood

6
Tôi hầu như luôn luôn bỏ qua main's return 0trong testcase, nhưng hầu như luôn viết tự tài liệu hóa return EXIT_SUCCESStrong mã sản xuất. Đó là thực hành tốt!
Lightness Races in Orbit

30
đây có vẻ là câu trả lời tốt nhất đối với tôi - bất cứ thứ gì tương lai với macro hoặc mẫu vẫn không đảm bảo rằng biến đó không thể được sử dụng sau đó. Điều này vừa làm tắt tiếng cảnh báo vừa đảm bảo rằng thông số (không tên) không bao giờ được sử dụng.
Alnitak

51

<tuple>trong C ++ 11 , bao gồm std::ignoređối tượng sẵn sàng sử dụng , cho phép chúng tôi viết (rất có thể mà không áp đặt chi phí thời gian chạy):

void f(int x)
{
    std::ignore = x;
}

4
Xem xét điều này là trong thư viện tiêu chuẩn và do đó không liên quan đến việc phải viết các hàm tùy chỉnh, tôi muốn nói đây là giải pháp tốt nhất!
BrainStone

2
Lớp này là "Dành cho sử dụng với std :: tie khi giải nén std :: tuple", không dành cho trường hợp sử dụng này. Tôi sẽ nói đó là một giải pháp, nhưng có lẽ không phải là tốt nhất.
Maestro

1
Tôi thích điều này thực sự bỏ qua .. Ofc, nếu bạn có thể loại bỏ tham số, hãy loại bỏ điều này thay thế (đó sẽ là giải pháp tốt nhất trong mọi trường hợp).
nguy hiểm 89

32

Để "vô hiệu hóa" cảnh báo này, cách tốt nhất là tránh viết đối số, chỉ viết kiểu.

void function( int, int )
{
}

hoặc nếu bạn thích, hãy bình luận nó ra:

void function( int /*a*/, int /*b*/ )
{
}

Bạn có thể kết hợp các đối số có tên và không có tên:

void function( int a, int /*b*/ )
{
}

Với C ++ 17, bạn có mã định nghĩa thuộc tính [[could_unused]], như:

void function( [[maybe_unused]] int a, [[maybe_unused]] int b )
{
}

2
Tôi sử dụng để làm điều này, nhưng nó nhanh chóng trở thành một nỗi đau kể từ khi bạn không còn có thể nhận xét ra một vùng lớn của mã với/* ... */
Ponkadoodle

1
Đó là sự thật nhưng với các IDE hiện đại, tôi đoán nếu bạn chọn một khối để nhận xét tự động, nó sẽ thêm một loạt "//" ở đầu mỗi dòng. Đó là những gì Eclipse CDT làm. Cá nhân tôi chỉ sử dụng ví dụ đầu tiên không có tên. (ví dụ, bạn có thể đặt tên trong các khai báo trong tệp .h).
Nikko

5
@Wallacoloo Khi tôi muốn nhận xét một đoạn mã lớn, tôi sử dụng #if 0 ... #endif, được phép lồng vào nhau và không bao giờ xung đột với các nhận xét / * ... * / hiện có.
Dmitry Frank

1
@DmitryFrank Và hầu hết các trình chỉnh sửa và IDE đều hỗ trợ #if 0khối xám như một trường hợp đặc biệt ngay cả khi chúng không hỗ trợ intellisense bộ tiền xử lý đầy đủ.
Thomas

30

Không có gì tương đương, không.

Vì vậy, bạn bị mắc kẹt với các tùy chọn cũ giống nhau. Bạn có hài lòng khi loại bỏ hoàn toàn các tên trong danh sách tham số không?

int main(int, char**)

mainTất nhiên, trong trường hợp cụ thể , bạn có thể chỉ cần bỏ qua các tham số:

int main()

Ngoài ra còn có các thủ thuật triển khai cụ thể điển hình, chẳng hạn như GCC __attribute__((unused)).


14

Macro có thể không lý tưởng, nhưng chúng hoạt động tốt cho mục đích cụ thể này. Tôi muốn nói rằng hãy sử dụng macro.


6
+1: Trong trường hợp này, chúng không gây hại và giải quyết vấn đề. Tôi không thấy có lý do gì (ngoài câu thần chú vô căn cứ nực cười "không bao giờ sử dụng macro") để không sử dụng chúng ở đây.
Lightness Races in Orbit

1
Lợi ích của macro là gì khi bỏ qua hoàn toàn tên tham số?
Micha Wiedenmann

1
@MichaWiedenmann: Một số tham số chỉ có thể được sử dụng khi một số hằng số tiền xử lý được đặt (điển hình là trong Gỡ lỗi).
Matthieu M.

2
@MatthieuM: Tôi muốn gọi là macro MAYBE_UNUSED, vì lý do đó; Tôi thường không quan tâm nếu tôi đã nói "đừng lo lắng nếu tôi không sử dụng điều này bên dưới" nhưng dù sao thì hãy tiếp tục.
Lightness Races in Orbit

2
Ok, vì vậy điều chính xác có thể là gọi nó là "HIDE_UNUSED_WARNING". Nhưng tôi vẫn nghĩ rằng sử dụng macro ở đây là một ý tưởng hoàn toàn hợp lệ. Miễn là macro được đặt tên theo cách không gây nhầm lẫn và / hoặc xung đột với mã khác.
Mats Petersson

13

Bạn có gì để chống lại cách cũ và tiêu chuẩn?

void f(int a, int b)
{
  (void)a;
  (void)b;
  return;
}

Tôi thấy rằng một số trình biên dịch hài lòng với điều này, nhưng một số trình biên dịch lại kén hơn những trình biên dịch khác. Công việc Cross Platform cần được kiểm tra trong tất cả các hệ điều hành và trình biên dịch được nhắm mục tiêu để đảm bảo rằng tất cả chúng đều hài lòng với giải pháp.
Jesse Chisholm,

12

Không có gì mới.

Điều tốt nhất đối với tôi là nhận xét tên tham số trong việc triển khai. Bằng cách đó, bạn thoát khỏi cảnh báo, nhưng vẫn giữ lại một số khái niệm về tham số là gì (vì tên có sẵn).

Macro của bạn (và mọi cách tiếp cận truyền thành khoảng trống khác) có nhược điểm là bạn thực sự có thể sử dụng tham số sau khi sử dụng macro. Điều này có thể làm cho mã khó bảo trì hơn.


12

Tiêu đề Boost <boost/core/ignore_unused.hpp>(Boost> = 1.56) xác định mẫu hàm cho mục đích này boost::ignore_unused().

int fun(int foo, int bar)
{
  boost::ignore_unused(bar);
#ifdef ENABLE_DEBUG_OUTPUT
  if (foo < bar)
    std::cerr << "warning! foo < bar";
#endif

  return foo + 2;
}

PS C ++ 17 có [[maybe_unused]]thuộc tính để loại bỏ cảnh báo trên các thực thể không sử dụng.


1
[[maybe_unused]]là cách rõ ràng, tốt nhất hiện nay.
Tomilov Anatoliy

0

Tôi thực sự thích sử dụng macro cho việc này, vì nó cho phép bạn kiểm soát tốt hơn khi bạn có các bản dựng gỡ lỗi khác nhau (ví dụ: nếu bạn muốn xây dựng với các xác nhận được bật):

#if defined(ENABLE_ASSERTS)
  #define MY_ASSERT(x) assert(x)
#else
  #define MY_ASSERT(x)
#end

#define MY_UNUSED(x)

#if defined(ENABLE_ASSERTS)
  #define MY_USED_FOR_ASSERTS(x) x
#else
  #define MY_USED_FOR_ASSERTS(x) MY_UNUSED(x)
#end

và sau đó sử dụng nó như:

int myFunc(int myInt, float MY_USED_FOR_ASSERTS(myFloat), char MY_UNUSED(myChar))
{
  MY_ASSERT(myChar < 12.0f);
  return myInt;
}

0

Tôi có cách triển khai của riêng mình cho các phân đoạn mã quan trọng về thời gian. Tôi đã nghiên cứu một đoạn mã quan trọng về thời gian để làm chậm và nhận thấy việc triển khai này tiêu tốn khoảng 2% so với thời gian mã quan trọng mà tôi đã được tối ưu hóa:

#define UTILITY_UNUSED(exp) (void)(exp)
#define UTILITY_UNUSED2(e0, e1) UTILITY_UNUSED(e0); UTILITY_UNUSED(e1)
#define ASSERT_EQ(v1, v2) { UTILITY_UNUSED2(v1, v2); } (void)0

Mã quan trọng về thời gian đã sử dụng các ASSERT*định nghĩa cho mục đích gỡ lỗi, nhưng trong bản phát hành rõ ràng nó đã bị loại bỏ, nhưng ... Có vẻ như mã này tạo ra mã nhanh hơn một chút trong Visual Studio 2015 Update 3:

#define UTILITY_UNUSED(exp) (void)(false ? (false ? ((void)(exp)) : (void)0) : (void)0)
#define UTILITY_UNUSED2(e0, e1) (void)(false ? (false ? ((void)(e0), (void)(e1)) : (void)0) : (void)0)

Lý do là trong false ?biểu thức kép . Bằng cách nào đó, nó tạo ra mã nhanh hơn một chút khi được phát hành với tối ưu hóa tối đa.

Tôi không biết tại sao điều này nhanh hơn (có vẻ như một lỗi trong tối ưu hóa trình biên dịch), nhưng ít nhất nó là một giải pháp tốt hơn cho trường hợp mã đó.

Lưu ý : Điều quan trọng nhất ở đây là một đoạn mã quan trọng về thời gian sẽ bị chậm lại mà không có các xác nhận ở trên hoặc các macro không sử dụng được phát hành. Nói cách khác, false ?biểu thức kép giúp tối ưu hóa mã một cách đáng ngạc nhiên.


-1

windows.h xác định UNREFERENCED_PARAMETER :

#define UNREFERENCED_PARAMETER(P) {(P) = (P);}

Vì vậy, bạn có thể làm như thế này:

#include <windows.h>
#include <stdio.h>
int main(int argc, char **argv) {
  UNREFERENCED_PARAMETER(argc);
  puts(argv[1]);
  return 0;
}

Hoặc bên ngoài Windows:

#include <stdio.h>
#define UNREFERENCED_PARAMETER(P) {(P) = (P);}
int main(int argc, char **argv) {
  UNREFERENCED_PARAMETER(argc);
  puts(argv[1]);
  return 0;
}

6
Đây không phải là một lựa chọn tốt, vì operator=có thể có tác dụng phụ.
Tamás Szelei
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.