Is nodiscard necessary on operators? - c++

Is the [[nodiscard]] attribute necessary on operators? Or is it safe to assume the compiler will emit a warning like it does for most suspiciously discarded things?
E.g. an overloaded operator+, should one apply the attribute? What about special operators like function-cast operators or new operators? When is it pedantic?

Let me cite the following paper by N.Josuttis: "[[nodiscard]] in the library" (with some omissions, see the full paper):
C++17 introduced the [[nodiscard]] attribute. The question is, where to apply it now in the standard library. It should be added where:
not using the return value always is a “huge mistake” (e.g. always resulting in resource leak),
not using the return value is a source of trouble and easily can happen (not obvious that something is wrong).
It should not be added when:
not using the return value is a possible/common way of programming at least for some input,
not using the return value makes no sense but doesn’t hurt and is usually not an error.
So, [[nodiscard]] should not signal bad code if this
can be useful not to use the return value,
is common not to use the return value,
doesn’t hurt and probably no state change was meant that doesn’t happen.

It is never necessary to add the [[nodiscard]] attribute. From cppreference:
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.
Note the last part: "... the compiler is encouraged to issue a warning." The is no guarantee, as far as the standard is concerned, that there actually will be a warning. Its a quality of implementation issue. If your compiler does emit a warning (read the docs) and if you are treating such warnings as errors, then the [[nodiscard]] can be of great use.
It is pedantic to use the attribute on operators where discarding the return is only potentially an error. I would only use it when calling the operator and discarding the result is always a logic error. Many operators use the return value merely to enable chaining and the [[nodiscard]] would rather be an annoyance on such operators. There are cases where the decision is not so obvious and it is a matter of opinion and style what you choose.

Is nodiscard necessary on operators?
No. nodiscard and other attribures are optional.
Or is it safe to assume the compiler will emit a warning like it does for most suspiciously discarded things?
There is no guarantee about any warning in the language except when the program is ill formed.
I would also not assume warning without nodiscard because there are many cases where result of operation is intentionally discarded. A common example:
a = b; // result of assignment was discarded
In fact, if all discarded results resulted in a warning, then there would not be any purpose for the nodiscard attribure.

Related

Why clang-tidy suggests to add [[nodiscard]] everywhere?

I have a C++ project where clang-tidy is suggesting to add [[nodiscard]] everywhere. Is this a good practice ? The understanding I have is that [[nodiscard]] should be used only when ignoring the return value could be fatal for program. I have an object Car and it has a member const unsigned int m_ID. Should the getter unsigned int getID() have [[nodiscard]] ? clang-tidy suggests so.
EDIT:
Of course, I do not want to ignore a getter. BUT
My point is if every function that returns something should have a [[nodiscard]], then the attribute [[nodiscard]] is anyway redundant. Compiler can simply check all functions that return something.
This option is apparently "modernize-use-nodiscard", so you can deactivate that if you prefer.
It should be noted that the rules this option outlines are not the rules the C++ standard committee themselves use for when to apply [[nodiscard]]. Those rules being:
It should be added where:
For existing API’s
not using the return value always is a “huge mistake” (e.g. always resulting in resource leak)
not using the return value is a source of trouble and easily can happen (not obvious that something is wrong)
For new API’s (not been in the C++ standard yet)
not using the return value is usually an error.
It should not be added when:
For existing API’s
not using the return value is a possible/common way of programming at least for some input
for example for realloc(), which acts like free when the new site[sic] is 0
not using the return value makes no sense but doesn’t hurt and is usually not an error (e.g., because programmers meant to ask for a state change).
it is a C function, because their declaration might not be under control of the C++ implementation
This is why functions like operator new are [[nodiscard]], while functions like optional::value are not. There is a difference between being your code having a minor mistake and your code being fundamentally broken. [[nodiscard]], as far as the committee is concerned, is for the latter.
Note that container empty methods are a special case. They seem to fit the "do not use [[nodiscard]]" pattern, but because the name of empty is similar to the name for clear, if you don't use the return value of empty, odds are good that you meant to call clear.
Obviously, this cannot be known from just a declaration, so there's no way for Clang-Tidy to implement said rules.
Why clang-tidy suggests to add [[nodiscard]] everywhere?
clang-tidy doesn't suggest to add [[nodiscard]] everywhere. The cases where it is suggested are described in the documentation of the check.
Is this a good practice ?
Yes, using [[nodiscard]] is a good practice when discarding the result is likely a bug. That is the case quite often.
Should the getter unsigned int getID() have [[nodiscard]] ?
Can you imagine any use case where it would be useful to call that getter without using the returned value? If you are certain that such case won't exist, then you should use [[nodiscard]]. I think such case doesn't exist in the described example.
The understanding I have is that [[nodiscard]] should be used only when ignoring the return value could be fatal for program.
That's a rather conservative understanding. You can disable the check in question if you don't agree with it.

What would break if control-reaches-end-of-body were to return nullopt?

C++14 gave us automatic return type deduction, and C++17 has an optional<T> type template (or type constructor, if you will). Now, true, optional lives within the standard library, not the language itself, but - why not use it as return value from a non-void function when control reaches the end of the body? I would think that:
optional<int> foo(int x)
{
if (x > 0) return 2 * x;
}
should be perfectly valid and compilable syntax for a partial function, returning optional<int>.
Now, I know this is a bit of a crazy idea. My question is not whether you like it or not, but rather - suppose everyone on the committee really liked it for some strange reason. What would it break / conflict with?
Note: Of course if you specify a non-optional return value this can't work, but that doesn't count as breakage.
Think of functions that end with abort(); or a custom function that has the same effect. If the compiler cannot statically prove functions never reach the closing }, this would force the compiler to generate dead code, and is therefore in conflict with one of the principles of C++, namely the zero overhead principle: what you don't use, you don't pay for.
Special casing std::optional is ridiculous here. Users should be able to write their own first-class equivalent to std::optional.
Which means falling off the end of a function needs to involve using some kind of magic to figure out what the implicit return value should be.
The easiest magic is that falling-off-the-end is equivalent to return {}; In the case of optional, this is nullopt. If I read my standardese correctly, for int this is 0, and this matches the behavior of falling-off-the-end-of-main.
There are downsides. First, suppose you have a function:
int foo(bool condition) {
if (condition) return 7;
custom_abort(); // does not return, but not marked up with `[[noreturn]]`
}
This would cause the compiler to write a return {}; after custom_abort(); if the compiler cannot prove that abort doesn't return. This has a cost (in binary size at the least). Currently, the compiler is free to exclude any work required to return from foo after abort() and assume abort() will not return.
It is true that no valid programs will behave differently with this change, but what was previously undefined behavior becomes defined, and that can have costs.
We could approach this in a slightly different way:
int foo(bool condition) {
if (condition) return 7;
custom_abort();
this cannot be reached;
}
where we add in an explicit "this location cannot be reached" to C++.
Once added, we could then issue warnings for code paths that do not return, and in a later standard enforce the rule that all code paths must either assert they cannot be reached, or must return.
After such a transformation of the language was in place for a standard cycle or two, then implicit return {}; would be harmless, except for people who skipped over the return cannot happen phase of standardization.
Up to now, it is full Undefined Behavior. That means no valid existing code contains this construct. Adding well-defined behavior will therefore break no valid code. As for code that was broken, that may or may not be broken if your proposal would be accepted, but that's almost never a concern for WG21.
The main concern would be how it would interact with other language features. I don't see a conflict with constexpr; falling off the end of a constexpr function would give an empty constexpr optional<T>. The [[noreturn]] attribute obviously makes no sense. The [[nodiscard]] attribute affects the caller, not the implementation. Exceptions are not affected either. So on the whole, the proposal seems to stand on its own.
In a proposal to WG21, it might be worth suggesting a less radical alternative: make plain return; a valid alternative for return optional<T>{};

Need help regarding macro definition

Im reading c++ code, i have found such definition
#define USE_VAL(X) if (&X-1) {}
has anybody idea, what does it mean?
Based on the name, it looks like a way of getting rid of an "unused variable" warning. The intended use is probably something like this:
int function(int i)
{
USE_VAL(i)
return 42;
}
Without this, you could get a compiler warning that the parameter i is unused inside the function.
However, it's a rather dangerous way of going about this, because it introduces Undefined Behaviour into the code (pointer arithmetic beyond bounds of an actual array is Undefined by the standard). It is possible to add 1 to an address of an object, but not subtract 1. Of course, with + 1 instead of - 1, the compiler could then warn about "condition always true." It's possible that the optimiser will remove the entire if and the code will remain valid, but optimisers are getting better at exploiting "undefined behaviour cannot happen," which could actually mess up the code quite unexpectedly.
Not to mention that fact that operator& could be overloaded for the type involved, potentially leading to undesired side effects.
There are better ways of implementing such functionality, such as casting to void:
#define USE_VAL(X) static_cast<void>(X)
However, my personal preference is to comment out the name of the parameter in the function definition, like this:
int function(int /*i*/)
{
return 42;
}
The advantage of this is that it actually prevents you from accidentally using the parameter after passing it to the macro.
Typically it's to avoid an "unused return value" warning. Even if the usual "cast to void" idiom normally works for unused function parameters, gcc with -pedantic is particularly strict when ignoring the return values of functions such as fread (in general, functions marked with __attribute__((warn_unused_result))), so a "fake if" is often used to trick the compiler in thinking you are doing something with the return value.
A macro is a pre-processor directive, meaning that wherever it's used, it will be replaced by the relevant piece of code.
and here after USE_VAL(X) the space it is explain what will USE_VAL(X) do.
first it take the address of x and then subtract 1 from it. if it is 0 then do nothing.
where USE_VAL(X) will used it will replaced by the if (&X-1) {}

Why use 'function address == NULL' instead of 'false'?

Browsing among some legacy code I've found such function:
static inline bool EmptyFunc()
{
return (void*) EmptyFunc == NULL;
}
What are the differences from this one:
static inline bool EmptyFunc()
{
return false;
}
This code was created to compile under several different platforms, like PS2, Wii, PC... Are there any reason to use the first function? Like better optimization or avoiding some strange compiler misbehavior?
Semantically both functions are the same: they always return false*. Folding the first expression to a constant value "false" is completely allowed by the standard since it would not change any observable side-effects (of which there are none). Since the compiler sees the entire function it also free to optimize away any calls to it and replace it with a constant "false" value.
That is, there is no "general" value in the first form and is likely a mistake on the part of the programmer. The only possibility is that it exploits some special behaviour (or defect) in a specific compiler/version. To what end I don't know however. If you wish to prevent inlining using a compiler-specific attribute would be the correct approach -- anything else is prone to breaking should the compiler change.
(*This assumes that NULL is never defined to be EmptyFunc, which would result in true being returned.).
Strictly speaking, a function pointer may not be cast to a void pointer, what happens then is outside the scope of the standard. The C11 standard lists it as a "common extension" in J.5.7 (I suspect that the same applies in C++). So the only difference between the two cases in that the former is non-portable.
It would seem that the most likely cause of the former version is either a confused programmer or a confused compiler. We can tell for certain that the programmer was confused/sloppy by the lack of an explaining comment.
It doesn't really make much sense to declare a function as inline and then try to trick the compiler into not inlining the code by including the function address in the code. So I think we can rule out that theory, unless of course the programmer was confused and thought it made sense.

Should the conditional operator evaluate all arguments?

When writing this:
1: inline double f( double arg ) {
2: return arg == 0.0 ? 0.0 : 1./arg;
3: }
4: const double d = f( 0.0 );
The microsoft visual studio 2005 64-bit compiler came with
line 4: warning C4723: potential divide by 0
While you and I can clearly see that a div-by-zero is never going to happen...
Or is it?
The compiler is not able to statically analyze all code paths and take into account all possibilities all the time. Theoretically, complete analysis of a program behavior just by looking into its source code can provide a solution to halting problem, which is undecidable. Compilers have a limited set of static analysis rules to detect rules. The C++ standard does not require the compiler to issue such kind of warnings, so, no. It's not a bug. It's more like a nonexistent feature.
No, the conditional operator does not evaluate both arguments. However, a potential divide-by-zero, if a compiler can detect such a thing, is typically reported. It is not for nought that the standard takes up ~2 pages to describe the behavior of this operator.
From N-4411:
5.16 Conditional operator
1 Conditional expressions group
right-to-left. The first expression is
contextually converted to bool (Clause
4). It is evaluated and if it is true,
the result of the conditional
expression is the value of the second
expression, otherwise that of the
third expression. Only one of the
second and third expressions is
evaluated. Every value computation and
side effect associated with the first
expression is sequenced before every
value computation and side effect
associated with the second or third
expression.
Also, note:
3 Otherwise, if the second and third
operand have different types, and
either has (possibly cv-qualified)
class type, an attempt is made to
convert each of those operands to the
type of the other.
The example you have cited has the same type for both the second and the third expressions -- rest assured, only the first will be evaluated.
It's an obvious bug, beyond doubt.
The intent of the warning is NOT to warn about all divisions in a program. That would be far too noisy in any reasonable program. Instead, the intent is to warn you when you need to check an argument. In this case, you did check the argument. Hence, the compiler should have noted that, and shut up.
The technical implementation of such a feature is done by labelling variables in code branches with certain attributes. One of the most common attributes is the tri-state "Is null". Before the branch, arg is an external variable and arg [[Isnull]] is unknown. But after the check on arg there are two branches. In the first branch arg [[Isnull]] is true. In the second branch arg [[Isnull]] is false.
Now, when it comes to generating divide-by-zero and null pointer warnings, the [[IsNull] attribute should be checked. If true, you have a severe warning/error. If unknown, you should generate the warning shown above - a potential problem, beyond what the compiler can prove. But in this case, the [[isNull]] attribute is False. The compiler by the same formal logic as humans, knows that there is no risk.
But how do we know that the compiler is using such an [[Isnull]] attribute internally? Recall the first paragraph : without it, it would have to either warn always or never. We know it warns sometimes, ergo there must be an [[IsNull]] attribute.
the code for the division will be generated, hence the warning. but the branch will never be taken when arg is 0, so it is safe.
operator== for floating-point numbers is unsafe (i.e. you cannot trust it, due to rounding issues). In this specific case it is actually safe, so you can ignore the warning, but the compiler will not make such an analysis based on an operator whose results are somewhat unpredictable in the general case.
The conditional operator shouldn't evaluate all arguments. But I believe you could take arg almost equal to 0, so arg == 0.0 will be false, but 1./arg will give "division by zero" result. So I think that warning is useful here.
By the way, Visual C++ 2008 doesn't give such warning.
In addition to the other comments: the warning is generate by the compiler, the dead branch is removed by the optimizer which runs later - possibly even at link stage.
So no, it's not a bug. The warning is an additional service provided by the compiler, not mandated by the standard. It's an unfortunate side effect of the compiler / linker architecture.
You might be able to avoid the warning using the Microsoft-specific __assume keyword. I'm not sure if you can tie it in with the conditional operator. Otherwise something like
if (arg == 0.0){
return 0.0;
}
else {
__assume(arg != 0.0);
return 1./arg;
}
may be worth a shot. Or, of course, just silence the warning during this function, with the appropriate #pragma.