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.
12345678[[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:
123warning: 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:
123456789struct [[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:
123456789use_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:
1static_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:
12<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