Coding Memo

[[nodiscard]] 리턴 값 무시 방지 속성 (C++17 ~) 본문

Language/C++

[[nodiscard]] 리턴 값 무시 방지 속성 (C++17 ~)

minttea25 2025. 5. 5. 13:26

"반드시 이 함수는 리턴값을 무시하면 안된다!"

"이 함수의 리턴값을 무시하면 사용 의미가 없다!"

 

위 경우에 리턴값을 무시하지 않도록 컴파일 시 경고를 줄 수 있는 기능이 C++17부터생겼다.

 

 `[[nodiscard]]`

 

[[nodicard]]를 함수나 타입 앞에 붙이게 되면, 이 함수나 타입을 사용하고 리턴 값을 무시했을 때, 경고를 줄 수 있다.

 

다음과 같은 특징이 있다.

1. 함수 뿐만 아니라, 타입에도 적용 가능

2. C++20 부터는 경고 메시지 (진단 메시지)를 직접 추가할 수 있음반드시 이 함수는 리턴값을 무시하면 안된다!"

 

"이 함수의 리턴값을 무시하면 사용 의미가 없다!"

 

 

 

위 경우에 리턴값을 무시하지 않도록 컴파일 시 경고를 줄 수 있는 기능이 C++17부터생겼다.

함수나 타입 앞에 아래 속성을 추가하면 된다. 

 

[[nodiscard]]

 

[[nodicard]]를 함수나 타입 앞에 붙이게 되면, 이 함수나 타입을 사용하고 리턴 값을 무시했을 때, 경고를 줄 수 있다.

 

 

 

다음과 같은 특징이 있다.

 

1. 함수 뿐만 아니라, 타입에도 적용 가능

2. C++20 부터는 경고 메시지 (진단 메시지)를 직접 추가할 수 있음

 

이 [[nodicard]] 속성을 어떤 경우에 사용하면 좋을지 생각해보자.

 

먼저, 클래스 내의 멤버변수나, 외부 참조에 대한 값을 변경 시키지 않는 함수 (e.g. const 함수)는 반환값을 사용하지 않다면 의미가 없으므로 사용처에 알맞을 것이다. (pure-function)

조금 더 깊히 생각한다면, 어떤 메모리를 로드하거나, 상태를 반환 또는 특정 기능을 하는 핸들을 반환하는 함수나 타입에서도 사용 할 수 있을 것이다. 윈도우 함수를 생각하면 간단하다. HRESULT를 반환하는 함수를 실행 시키고, 결과 값을 확인하지 않는다면, 실행이 제대로 되었는지 알 수가 없고, 결과 값도 사용할 수 없을 것이다. 마찬가지로, IOCP와 관련된 핸들을 가져오는 함수도, 핸들을 가져오지않는다면, 함수를 실행시킨 의미가 없을 것이다.    

   

Note: MSVC에서는 `__DISCARD` 라는 매크로 키워드를 제공하기도 한다. (실제로 [[nodiscard]] 가 정의되어 있다.)

Notes2: Pure Function이란, 함수 실행 시, 외부에 영향을 주지 않는 함수를 말한다. 

 


 1. [[nodiscard]] 함수에 적용

 

양쪽에 괄호가 2개씩인 것에 유의하자.

다음은 간단한 예시이다.

[[nodiscard]]
static int sum( const int a, const int b )
{
    return a + b;
}

 

이렇게 하면, sum을 사용했을 때, return 값을 사용하지 않는다면 경고(C4834)가 나타난다.

nodiscard.cpp(11,8): warning C4834: discarding return value of function with [[nodiscard]] attribute

 

만약, C++20 이상에서 진단메시지를 추가한다면...

[[nodiscard("Check the return value!")]]
static int sum( const int a, const int b )
{
    return a + b;
}
nodiscard.cpp(11,8): warning C4858: discarding return value: Check the return value!

 

경고 메시지가 지정한대로 나타난다!


2. [[nodiscard]] 타입에 적용

실제로 사용되고 있거나, 사용될 수 있는 좋은 예시라고 생각한다. 

struct [[nodiscard]] Result
{
    // 0: success
    // > 0 : failed with reason
    int result = 0;
    std::string reason;
};

Result LoadTexture( const std::wstring& path, void*& ptr )
{
    Result ret;
    // ...
    return ret;
}

void LoadAll()
{
    void* pTexture = nullptr;
    LoadTexture( L"../test.png", pTexture ); // warning C4834
    // ...
}

 

Result라는 구조체 타입에 [[nodiscard]]를 사용한 코드이다.

LoadTexture를 호출한 이후에 반환값(Result)를 무시하고 있는 LoadAll 내부에서 컴파일 경고가 나타나게 된다.

 

실제로, LoadTexture를 하고, 이 함수가 성공했는지 실패 했는지 알아야, pTexture라는 변수를 사용할 수 있을 것이다.

(만약 실패 했다면, pTexture의 값은 nullptr 그대로이다. 즉, 성공여부를 확인하지 않은 상태에서 pTexture를 사용한다면 nullptr 참조가 일어나는 큰 문제가 발생할 것이다.)

 

따라서, 위와 같은 실수 및 문제를 방지하기 위해서 [[nodiscard]]를 사용하는 것이다.

 

아래와 같이 좀 더 좋은코드로 바꿀 수 있다.

void LoadAll()
{
    void* pTexture = nullptr;

    auto result = LoadTexture( L"../test.png", pTexture );
    if( result.result != 0 )
    {
        std::cout << "Failed to load texture: " << result.reason << std::endl;
        return;
    }

    // Do something with pTexture
    // ...
}

  


리턴 값이 중요한 함수나 타입에는 [[nodiscard]] 속성을 붙여서 실수나, 문제를 방지하자!

별거 아닌 것 같아보이지만, 은근 유용하게 이용될 수도 있다.

 

추가적인 정보는 아래 문서를 참고해도 될 것 같다.

https://en.cppreference.com/w/cpp/language/attributes/nodiscard

 

C++ attribute: nodiscard (since C++17) - cppreference.com

If a function declared nodiscard or a function returning an enumeration or class declared nodiscard by value is called from a discarded-value expression other than a cast to void, the compiler is encouraged to issue a warning. [edit] Syntax [[nodiscard]] (

en.cppreference.com