사용하지 않는 변수에 대한 경고를 가장 잘 끄는 방법은 무엇입니까?


237

크로스 플랫폼 응용 프로그램이 있으며 일부 함수에서 함수에 전달 된 모든 값이 사용되는 것은 아닙니다. 따라서 GCC에서 사용되지 않은 변수가 있음을 알리는 경고가 표시됩니다.

경고를 코딩하는 가장 좋은 방법은 무엇입니까?

함수 주위의 #ifdef?

#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
{

이것은 추악하지만 컴파일러가 선호하는 방식 인 것 같습니다.

또는 함수 끝에서 변수에 0을 할당합니까? (컴파일러 경고를 침묵시키기 위해 프로그램 흐름에서 무언가를 변경하기 때문에 싫어합니다).

올바른 방법이 있습니까?


7
지난 11 월에 비슷한 질문을했다는 것을 깨달았습니다. 이것이 친숙해 보이는 이유입니다! ;) stackoverflow.com/questions/308277/…
Alex B

9
두 컴파일러 모두에 대해 주석 처리하지 않는 이유는 무엇입니까? 인자 저장 한에 사용되지 않는 경우, 아마 ... 다른 한편으로는 사용되지 않을 것이다
로저 Lipscombe

12
Qt에는 이것에 대한 Q_UNUSED매크로 가 있다는 것을 알아야합니다 . 설명서에서 확인하십시오.
Evan Teran

1
C 솔루션은 C ++에서도 잘 작동합니다. stackoverflow.com/a/3599170/1904815
JonnyJD

컴파일러 별 빌드 플래그를 사용할 수있는 경우 사용하지 않은 매개 변수도 옵션이 될 수 있습니다.
Code Abominator

답변:


327

컴파일러가 사용하는 것을 볼 수 있도록 " (void)var;"표현식 (아무것도하지 않음) 에 넣을 수 있습니다 . 이것은 컴파일러간에 이식 가능합니다.

예 :

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

또는,

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

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

22
+1-여전히 변수가있는 경우에도 왜 변수를 사용하지 않는지 문서화합니다.
Tobias Langner

18
이것이 Q_UNUSED원칙적으로 구현되는 방식 입니다.
Dmitry Volosnykh

11
@Cameron은 단순히 C ++에서 매개 변수 이름을 생략 할 수 있습니다. 템플릿으로 작성하면 C에서 사용되지 않으므로 캐스트 투 보이드 트릭이 필요하지 않습니다.
Alex B

13
그냥 #define UNUSED(expr) (void)(expr)(작업하지 않고) 작동해야합니다.
JonnyJD

7
variadic 템플릿에 대해 어떻게해야하는지 궁금합니다. 에서 template<typename... Args> void f(const Args&... args)내가 쓸 수 없습니다 (void)args;또는 (void)args...;때문에 둘 다 구문 오류가 있습니다.
panzi

101

GCC와 Clang에서는 __attribute__((unused))전 처리기 지시문을 사용 하여 목표를 달성 할 수 있습니다.
예를 들면 다음과 같습니다.

int foo (__attribute__((unused)) int bar) {
   return 0;
}

1
콜백 함수에 가장 적합한 솔루션입니다.
소닉 아톰



39

현재 솔루션이 가장 좋습니다-매개 변수 이름을 사용하지 않으면 주석 처리하십시오. 이는 모든 컴파일러에 적용되므로 GCC를 위해 특별히 사전 처리기를 사용할 필요가 없습니다.


7
이 답변을 강화하기 위해 #ifdef가 필요하지 않고 사용하지 않은 매개 변수 이름을 주석 처리하십시오.
quamrana

4
매개 변수가 콜백의 일부인 경우 주석 처리하면 컴파일이 중단됩니다 (그래서 왜 g++경고 하는지 확실하지 않습니다 .) 그런 경우 권장하는 것은 무엇입니까?
Drew Noakes

1
사용하지 않는 매개 변수 / * commented * /를 가진 인라인 가상 메소드를 상상해보십시오. 인터페이스의 클라이언트는 대부분의 IDE에서 자동 완성 중에 매개 변수 이름을 볼 수 없습니다. 이 경우 UNUSED () 솔루션은 더 깨끗하지만 더 편리합니다.
cbuchart

나는 간단하게 작성하는 것이 더 좋다고 생각하며, 의견을
밝히는

26

C ++ 17 업데이트

C ++ 17에서는 [dcl.attr.unused] 에서 다루는 [[maybe_unused]] 속성을 얻 습니다.

속성 토큰 maybe_unused는 이름 또는 엔티티가 의도적으로 사용되지 않았 음을 나타냅니다. 각 속성 목록에 최대 한 번 나타나야하며 속성 인수 절이 없어야합니다. ...

예:

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

구현은 NDEBUG의 정의 여부에 관계없이 b가 사용되지 않음을 경고해서는 안됩니다. — 끝 예제]

다음 예의 경우 :

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

clang과 gcc는 모두 baruned_bool에 대해 -Wall -Wextra 를 사용하여 진단을 생성합니다 ( 실제 참조 ).

[[maybe_unused]]를 추가 하면 진단 기능이 꺼집니다 .

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

그것을 참조하십시오 .

C ++ 17 이전

C ++ 11 에서는 사용되지 않는 변수를 캡처 UNUSED하여 람다 식 ( Ben Deane을 통해 )을 사용하여 대체 형식의 매크로를 만들 수 있습니다 .

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

다음 예제에서 람다 식의 즉각적인 호출을 최적화해야합니다.

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

Godbolt 에서 호출이 최적화되었음을 알 수 있습니다.

foo(int):
xorl    %eax, %eax
ret

5
C ++ 11을 언급하고 매크로를 제시합니까?! 아야! 아마도 기능을 사용하는 것이 더 깨끗할까요? template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); }함수에 람다를 사용할 수도 있다고 생각합니다.
Alexis Wilke

godbolt은 훌륭한 자원이다
야노

5
[&x]{}()실제로 경고를 침묵시키지 않지만 대신 호출자 함수에서 람다로 경고를 전달합니다. 컴파일러가 이것을 경고로 식별 할 때까지 시간이 걸리지 만 clang-tidy는 이미 캡처 목록에서 사용되지 않는 변수에 대해 불평합니다.
nVxx

25

더 깨끗한 방법은 변수 이름을 주석 처리하는 것입니다.

int main(int /* argc */, char const** /* argv */) {
  return 0;
}

8
doxygen이 있고 매개 변수를 문서화하려는 경우에는 좋지 않습니다.
Alexis Wilke

18
@AlexisWilke : 그것은 doxygen, IMO의 버그로 인정 될 것입니다
6502

3
doifgen이 이름을 볼 수 있고 실제 컴파일러가 int main (int YOUR_PROJECT_UNUSED (argc), ...)을 통해 이름을 볼 수 없도록 #ifdef DOXYGEN에 조건부로 YOUR_PROJECT_UNUSED (argname)을 #define 할 수 있습니다. 훌륭하지는 않지만 작동합니다.
mabraham

중첩 된 주석이 많은 코드 블록을 주석 처리하는 것은 매우 고통 스럽습니다. (컴파일러는 모든 것에 대해 불평합니다).
Jeff McClintock 2016 년

@JeffMcClintock은 단지 한 줄 주석을 사용합니다. 알맞은 편집기는 수직 블록 편집을 지원합니다 (예 : Vim의 [Ctrl] + [V]). 그렇지 않으면 #if 0 / #endif블록 주석을 사용하십시오 .
Ruslan

24

동료는이 좋은 작은 매크로에 저를 지적 여기

쉽게하기 위해 아래 매크로를 포함하겠습니다.

#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))

12
"nice" "macro" "c ++"-pick 2
Jeff McClintock

23

기본적으로 이러한 경고를 표시하지 않습니다. 이 경고는 명시 적으로 -Wunused-parameter컴파일러에 전달하거나 암시 적으로 전달 -Wall -Wextra(또는 플래그의 다른 조합)을 통해 설정해야합니다.

사용하지 않는 매개 변수 경고는 -Wno-unused-parameter컴파일러 에 전달 하여 간단히 억제 할 수 있지만,이 비활성화 플래그는 컴파일러 명령 행에서이 경고에 대한 가능한 활성화 플래그 뒤에 와야 적용 할 수 있습니다.


2
비록 이것이 질문에 대한 최선의 답변이 아닐 수도 있지만 (질문은 경고를 피하는 방법이 아니라 비활성화하는 방법이 아니기 때문에) Google에서 온 사람들이 (나 같은) 이 경고를 비활성화하려면 "). 당신의 답변에 감사드립니다.
mozzbozz

13

하나 이상의 매개 변수를 사용하지 않는 것으로 선언하는 매크로가 적고 이식 가능한 방법 :

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

int main(int argc, char* argv[])
{
    unused(argc, argv);
    return 0;
}

매우 좋지만 C ++ 11 (또는 최신)이 필요합니다.
Paul R

경고를 없애기 위해 (템플릿을 사용하여) 컴파일 시간을 희생하고 싶지 않기 때문에이 답변에 투표했습니다.
Konrad Kleine

@ KonradKleine : 컴파일 시간이 얼마나 걸릴까요? 내 컴퓨터에서 테스트하면 10 분의 1 초에 수천 개의 미사용 () 호출을 실행할 수 있습니다.
Daniel McLaury

@DanielMcLaury 이것은 내 추측 일 뿐이며 실험을하지 않았습니다.
Konrad Kleine

8

전 처리기 지시문을 사용하는 것은 대부분 악으로 간주됩니다. 해충처럼 피하는 것이 이상적입니다. 컴파일러가 코드를 이해하도록하는 것은 쉽다는 점을 기억하십시오. 다른 프로그래머가 코드를 이해하는 것이 훨씬 어렵습니다. 여기에 이와 같은 수십 가지 사례가 있으며, 나중에 자신이나 다른 사람이 읽기 어려운 경우가 있습니다.

한 가지 방법은 매개 변수를 일종의 인수 클래스로 묶는 것입니다. 그런 다음 변수의 하위 집합 만 사용하거나 (실제로 0을 할당하는 것과 동일) 각 플랫폼에 대해 해당 인수 클래스의 다른 전문화를 가질 수 있습니다. 그러나 이것은 그만한 가치가 없을 수 있습니다. 적합한 지 분석해야합니다.

불가능한 템플리트를 읽을 수있는 경우 "예외 C ++"책에서 고급 팁을 찾을 수 있습니다. 코드를 읽는 사람들이 그 책에서 가르치는 미친 것들을 포괄하는 스킬 셋을 얻을 수 있다면, 쉽게 읽을 수있는 아름다운 코드를 갖게 될 것입니다. 컴파일러는 또한 전처리로 모든 것을 숨기지 않고 수행중인 작업을 잘 알고 있습니다.


5
"전 처리기 지시문을 사용하는 것은 대부분 악으로 간주됩니다." 정말? 누구에 의해?
Graeme Perrow

12
범위에 관심이 있거나, 올바르게 디버깅 할 수있는 사람, 또는 그 정신이없는 사람.
Bill

2
@Graeme, 우리는 그것의 4 줄만 볼 때 무해 해 보이지만, 그 주위에 퍼지면 두통이 발생합니다. #ifdef는 기본적으로 컴파일러가 하나만 볼 수있는 여러 버전의 소스 코드를 넣을 수 있도록합니다. Bill이 언급했듯이 디버깅도 어렵습니다. 다양한 책과 블로그에서 전 처리기 지시문의 악에 대해 읽었으며 직접 경험해 보았습니다. 물론 모든 것이 상대적입니다. 전 처리기 지시문은 다른 결과가 더 나빠질 수 있기 때문에 단순히 의미가 있습니다.
Ben Dadsetan

1
남용은 나쁘지만 #define UNUSED(expr) (void)(expr)적절하다고 부릅니다 .
JonnyJD

7

먼저 경고는 헤더 파일이 아닌 소스 파일의 변수 정의에 의해 생성됩니다. API 문서를 생성하기 위해 doxygen과 같은 것을 사용하고 있기 때문에 헤더는 깨끗하게 유지 될 수 있습니다.

소스 파일에서 구현이 완전히 다르다고 가정합니다. 이 경우 문제가되는 매개 변수를 주석 처리하거나 매개 변수를 작성할 수 있습니다.

예:

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

이것은 암호처럼 보일 수 있으므로 UNUSED와 같은 매크로를 정의했습니다. MFC가 한 방식은 다음과 같습니다.

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

이와 같이 디버그 빌드에서 여전히 경고가 표시되는 것이 도움이 될 수 있습니다.


4

항상 매개 변수 이름을 주석 처리하는 것이 안전하지 않습니까? 그렇지 않은 경우 다음과 같은 작업을 수행 할 수 있습니다

#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))

그건 덜 추한.


4
C ++에서 매개 변수 이름이 필수는 아니라는 사실은 경고를 막는 표준적이고 쉬운 방법을 제공하는 것입니다.
AProgrammer

1
@hacker, 결코 말하지 않았습니다. 나는 C와 C ++의 차이점을 지적하는 경향이 있습니다. 특히 공통 부분 집합이라고 생각되는 지역에있을 때 ... 혼합 코드베이스로 작업하고 있기 때문에 습관입니다.
AProgrammer

4

나는 (void)param2경고를 침묵시키는 대신에 이것을 보았습니다 .

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

C ++ 11에서 추가 된 것 같습니다


컴파일 후 무시되지 않고 무언가를하는 것처럼 보입니다.
최규현

3

사용할 UNREFERENCED_PARAMETER(p)수 있습니다. Windows 시스템의 WinNT.h에 정의되어 있으며 gcc에 대해 쉽게 정의 할 수 있음을 알고 있습니다 (아직없는 경우).

UNREFERENCED PARAMETER(p) 로 정의된다

#define UNREFERENCED_PARAMETER(P)          (P)

WinNT.h에서.


2

컴파일러의 플래그를 사용하십시오 (예 : GCC의 경우 플래그). -Wno-unused-variable


1

당신이 사용할 수있는 __unused변수가 사용되지 않을 수 있음을 컴파일러에게.

- (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
}

2
어떤 컴파일러? __unused표준 C ++이 아니기 때문에 요점까지는 게시 한 것이 아닙니다 ... Objective-C입니다. 따라서이 답변은 특정 컴파일러에만 유용하며 코드를 이식 할 수 없으며 실제로 사용자 코드는로 시작하는 식별자를 사용하지 않기 때문에 실제로 유효하지 않습니다 __. 이는 구현을 위해 예약되어 있습니다.
underscore_d

1

C ++ 11에서는 이것이 사용중인 솔루션입니다.

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;
}

휴대용 (적어도 최신 msvc, clang 및 gcc에서) 휴대 가능하고 최적화가 활성화 된 경우 추가 코드를 생성하지 않는 것으로 확인되었습니다. 최적화가 없으면 추가 함수 호출이 수행되고 매개 변수에 대한 참조가 스택에 복사되지만 관련된 매크로는 없습니다.

추가 코드가 문제가되면이 선언을 대신 사용할 수 있습니다.

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

그러나 그 시점에서 매크로는 더 나은 가독성을 제공합니다.

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

1

이것은 잘 작동하지만 C ++ 11이 필요합니다.

template <typename ...Args>
void unused(Args&& ...args)
{
  (void)(sizeof...(args));
}

1
C ++ 14가 필요하고 C ++ 11에서는 작동하지 않습니까? 나는 아무것도 볼 수 없습니다. 또한 ALLCAPS매크로를 제외하고는 추악하고 바람직하지 않게 보이도록 매크로를 제외하고는 사용하지 않는 것이 좋지만 실제로는 static_cast더 좋을 것 외에는 나쁘지 않습니다 .
underscore_d

0

제시된 답변의 대부분은 로컬 미사용 변수에 대해서만 작동하며 미사용 정적 전역 변수에 대해 컴파일 오류가 발생합니다.

또 다른 매크로는 사용되지 않은 정적 전역 변수의 경고를 억제해야했습니다.

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;
}

익명 네임 스페이스의 비 정적 전역 변수에 대해서는 경고가보고되지 않기 때문에 작동합니다.

그래도 C ++ 11이 필요합니다

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

0

롤! 나는 이것에 대해 카오스에 의해 손상된 모든 이단자들을 드러내는 SO에 대한 또 다른 질문이 있다고 생각하지 않습니다!

C ++ 17과 관련하여 C ++ 핵심 가이드 라인에 명확한 지침이 있습니다. AFAIR, 2009 년에이 옵션은 현재뿐만 아니라 현재 사용 가능합니다. 누군가가 그것이 Doxygen의 버그로 간주되면 Doxygen에 버그가 있다고 말합니다


-14

경고와 관련된 문제가 보이지 않습니다. 컴파일러 xy가 여기에 (올바른) 경고를 발행하지만 메소드 z가 함수 z에 필요하다는 것을 메소드 / 함수 헤더에 문서화하십시오.

경고는 정확하므로 끄지 않아도됩니다. 프로그램을 무효화하지는 않지만 이유가 있음을 문서화해야합니다.


20
문제는 수백 또는 수천 개의 경고가있는 경우 유용한 경고를 놓칠 수 있다는 것입니다. (두 번 나는 수만 건의 경고를 겪고 대부분을 제거하고 심각한 오류를 암시 한 몇 가지 유용한 정보를 한 번 발견했습니다.) 가능하면 가장 높은 경고 수준에서 경고없이 컴파일하는 것이 좋습니다.
sbi

4
작년에 작업 한 프로젝트에서 가장 높은 경고 수준을 설정하고 ~ 10,000 경고가 발생했습니다. 수십 명만이 실제로 도움이되었습니다. 그 중에는 수십 개의 정말 불쾌한 버그가 숨겨져 있었지만 실제로 몇 가지 심각한 버그를 수있을 정도로 코드베이스를 정리하는 데 몇 주가 걸렸습니다 . 경고 수준이 항상 높아졌고 코드베이스에 경고가없는 경우 이러한 오류는 코드에 영향을 미치지 않습니다.
sbi

1
죄송하지만 프로젝트 후반에 정적 코드 분석 (컴파일러 만하더라도 사용 가능한 도구 사용)은 전체 프로그램을 프로그래밍하는 것과 약간 같으며 완료되면 컴파일을 누르고 오류가 없기를 바랍니다.
Tobias Langner

2
@Richard : 수천 개의 소스 파일로 프로젝트를 진행했습니다. 여기저기서 약간의 경고, 심지어 잘 문서화 된 경고가 빠르게 추가됩니다. 수백 또는 수천 대가 아닌 빌드 중에 수십 개의 경고 만 깜박이더라도 새로운 경고인지 문서화 된 것인지 너무 많은 시간이 걸리며 결국에는 하지 마라. 따라서 : 경고가 0이 아닌 가장 높은 경고 수준에서 컴파일하십시오. 발생하는 모든 경고는 즉시 확인되고,보고, 수정되거나 억제됩니다.
sbi

2
@ sbi : 컴파일러에 대한 가장 높은 경고 수준의 토리 닝은 일종의 정적 코드 분석입니다. 정적 코드 분석은 코드를 실행하지 않고 코드를 읽고 정보를 빼는 것입니다. 이것이 컴파일러가 경고 규칙을 확인할 때하는 일입니다.
Tobias Langner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.