Thursday, 8 November 2018

Warnings Series - Unused Result

In this series of posts I'm discussing how compiling with multiple compilers and warnings enabled can help to catch errors at development time. In the last post I discussed how to catch format errors and use compiler specific methods to annotate your code. In this post, I'm going to look at a standards compliant way to annotate your code to help the compiler identify potential errors.

Background

Starting with C++11, it is possible to add attribute specifiers to your code. These can give both your users and the compiler hints on how to treat your code. An example of such an attribute is the deprecated attribute to warn that some code (e.g. class, namespace, or function) should no longer be used and may be removed in the future.

Unused Result

The unused-result warning is issued when a function that uses the [[nodiscard]] attribute specifier is called and the returned value is not used. This attribute specifier was added to the language in C++17. It can be used to annotate functions, classes and enums.

nodiscard functions

The [[nodiscard]] attribute will encourage the compiler to issue a warning if a function declared nodiscard has it's returned value not captured. For example, the following code will issue an unused result warning.

1
2
3
4
5
6
7
8
[[nodiscard]] int* allocate_int()
{
    return new int(1);
}
 
void test() {
    allocate_int();
}

In this code, the return value of allocate_int is a raw pointer and should always be deleted. By adding the [[nodiscard]] attribute, we are telling the compiler to warn users of the function if they ignore the value. An example of such a warning from Clang is:

1
2
3
warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
    allocate_int();
    ^

Note: This only checks that the returned value is captured and not that it is used correctly.

nodiscard classes

The [[nodiscard]] attribute can also be added to classes (and enums) to warn when they are returned by value and ignored. For example:

1
2
3
4
5
6
7
8
9
struct [[nodiscard]] use_me { };
 
use_me do_stuff() {
    return use_me{};
}
 
void test_use_me() {
    do_stuff();
}

This will issue a warning that the use of do_stuff in test_use_me is not using the returned value from that function. This is only applicable when you return the class by value. For example, the following code will not issue a warning:

1
2
3
4
5
6
7
8
9
use_me USE_ME{};
 
use_me& do_more_stuff() {
    return USE_ME;
}
 
void test_use_me_ref() {
    do_more_stuff();
}

Ignoring nodiscard

If a function is marked [[nodiscard]] but for some reason you know you can safely ignore the result, you can use a void cast to ignore the value. In the case of our do_stuff function above, we could call it as below to safely ignore the warning:

1
static_cast<void>(do_stuff());

While C style casts are often discouraged in new code, for a case such as this it can make for easier to read code.

1
(void)do_stuff();

Catching the error

Clang

With newer versions of Clang, you will receive the unused-result warning when compiling with C++11, 14 or 17 on all warning levels. With older versions prior to v3.9, you may receive a warning that the nodiscard attribute is not known.

GCC

Similar to Clang, with newer versions of GCC you will receive the unused-result warning when compiling with the C++11, 14 or 17 standards on all warning levels. For GCC versions earlier than GCC 7, you will receive a the unknown-attribute warning.

MSVC

On MSVC 2018, you will receive the warning C4834 when compiling with the /std:c++latest flag. For example:

1
2
<source>(12): warning C4834: discarding return value of function with 'nodiscard' attribute
<source>(25): warning C4834: discarding return value of function with 'nodiscard' attribute

Earlier versions of MSVC, do not appear to display any related warnings, however, they do ignore the unknown attribute and allow the code to compile.

Comparing the errors

To view the errors from each compiler you can use godbolt to compare the output.

No comments:

Post a Comment