I've recently read about [[nodiscard]] in C++17, and as far as I understand it's a new feature (design by contract?) which forces you to use the return value. This makes sense for controversial functions like std::launder (nodiscard since C++20), but I wonder why std::move isn't defined like so in C++17/20. Do you know a good reason or is it because C++20 isn't finalised yet?
The MSVC standard library team went ahead and added several thousand instances of [[nodiscard]] since VS 2017 15.6, and have reported wild success with it (both in terms of finding lots of bugs and generating no user complaints). The criteria they described were approximately:
Pure observers, e.g. vector::size(), vector::empty, and even std::count_if()
Things that acquire raw resources, e.g. allocate()
Functions where discarding the return value is extremely likely to lead to incorrect code, e.g. std::remove()
MSVC does mark both std::move() and std::forward() as [[nodiscard]] following these criteria.
While it's not officially annotated as such in the standard, it seems to provide clear user benefit and it's more a question of crafting such a paper to mark all the right things [[nodiscard]] (again, several thousand instances from MSVC) and apply them -- it's not complex work per se, but the volume is large. In the meantime, maybe prod your favorite standard library vendor and ask them to [[nodiscard]] lots of stuff?
AFAIK P0600R1 is the only proposal for adding [[nodiscard]] to the standard library that was applied to C++20. From that paper:
We suggest a conservative approach:
[...]
It should not be added when:
[...]
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
[...]
doesn’t hurt and probably no state change was meant that doesn’t happen
So the reason is that the standard library uses a conservative approach and a more aggresive one is not yet proposed.
Related
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.
I've recently read about [[nodiscard]] in C++17, and as far as I understand it's a new feature (design by contract?) which forces you to use the return value. This makes sense for controversial functions like std::launder (nodiscard since C++20), but I wonder why std::move isn't defined like so in C++17/20. Do you know a good reason or is it because C++20 isn't finalised yet?
The MSVC standard library team went ahead and added several thousand instances of [[nodiscard]] since VS 2017 15.6, and have reported wild success with it (both in terms of finding lots of bugs and generating no user complaints). The criteria they described were approximately:
Pure observers, e.g. vector::size(), vector::empty, and even std::count_if()
Things that acquire raw resources, e.g. allocate()
Functions where discarding the return value is extremely likely to lead to incorrect code, e.g. std::remove()
MSVC does mark both std::move() and std::forward() as [[nodiscard]] following these criteria.
While it's not officially annotated as such in the standard, it seems to provide clear user benefit and it's more a question of crafting such a paper to mark all the right things [[nodiscard]] (again, several thousand instances from MSVC) and apply them -- it's not complex work per se, but the volume is large. In the meantime, maybe prod your favorite standard library vendor and ask them to [[nodiscard]] lots of stuff?
AFAIK P0600R1 is the only proposal for adding [[nodiscard]] to the standard library that was applied to C++20. From that paper:
We suggest a conservative approach:
[...]
It should not be added when:
[...]
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
[...]
doesn’t hurt and probably no state change was meant that doesn’t happen
So the reason is that the standard library uses a conservative approach and a more aggresive one is not yet proposed.
In TensorFlow's documentation, it is possible to find the following text:
// Not recommended
MatMul m(scope, a, b);
// Recommended
auto m = MatMul(scope, a, b);
I see no obvious benefit from using the "recommended" style. The first version is shorter at least. Also the "recommended" version might include more actions related to the unnecessary assignment operation.
I have read that documentation page no less than six times and still cannot get the rationale behind their reasoning.
Is this recommendation just a matter of style or may the second version have some benefits?
Also the "recommended" version might include more actions related to the unnecessary assignment operation.
There is no assignment. It's initialization. And any extra object that may exist in principle is elided entirely in any compiler worth using.
That recommendation you cite is inline with Herb Sutter's "Almost Always Auto" advice. The strongest point in favor of this style (which in full disclosure, I do not follow) is that it makes it impossible to leave a variable uninitialized.
auto foo; // ill-formed.
int foo; // indeterminate value
auto foo = int(); // a zero integer
Granted, a good static analysis tool can warn about it too, but it's still a very strong point that requires no additional analysis by a compiler or external tools.
Beyond that, the stylistic argument is that it also keep your code consistent with cases where you use auto for sanity's sake, such as
auto it = myVec.begin();
But YMMV on all counts. It is ultimately going to be a stylistic choice, and regardless of the chosen style, exceptions to both styles exist.
constexpr permits expressions which can be evaluated at compile time to be ... evaluated at compile time.
Why is this keyword even necessary? Why not permit or require that compilers evaluate all expressions at compile time if possible?
The standard library has an uneven application of constexpr which causes a lot of inconvenience. Making constexpr the "default" would address that and likely improve a huge amount of existing code.
It already is permitted to evaluate side-effect-free computations at compile time, under the as-if rule.
What constexpr does is provide guarantees on what data-flow analysis a compliant compiler is required to do to detect1 compile-time-computable expressions, and also allow the programmer to express that intent so that they get a diagnostic if they accidentally do something that cannot be precomputed.
Making constexpr the default would eliminate that very useful diagnostic ability.
1 In general, requiring "evaluate all expressions at compile time if possible" is a non-starter, because detecting the "if possible" requires solving the Halting Problem, and computer scientists know that this is not possible in the general case. So instead a relaxation is used where the outputs are { "Computable at compile-time", "Not computable at compile-time or couldn't decide" }. And the ability of different compilers to decide would depend on how smart their test was, which would make this feature non-portable. constexpr defines the exact test to use. A smarter compiler can still pre-compute even more expressions than the Standard test dictates, but if they fail the test, they can't be marked constexpr.
Note: despite the below, I admit to liking the idea of making constexpr the default. But you asked why it wasn't already done, so to answer that I will simply elaborate on mattnewport's last comment:
Consider the situation today. You're trying to use some function from the standard library in a context that requires a constant expression. It's not marked as constexpr, so you get a compiler error. This seems dumb, since "clearly" the ONLY thing that needs to change for this to work is to add the word constexpr to the definition.
Now consider life in the alternate universe where we adopt your proposal. Your code now compiles, yay! Next year you decide you to add Windows support to whatever project you're working on. How hard can it be? You'll compile using Visual Studio for your Windows users and keep using gcc for everyone else, right?
But the first time you try to compile on Windows, you get a bunch of compiler errors: this function can't be used in a constant expression context. You look at the code of the function in question, and compare it to the version that ships with gcc. It turns out that they are slightly different, and that the version that ships with gcc meets the technical requirements for constexpr by sheer accident, and likewise the one that ships with Visual Studio does not meet those requirements, again by sheer accident. Now what?
No problem you say, I'll submit a bug report to Microsoft: this function should be fixed. They close your bug report: the standard never says this function must be usable in a constant expression, so we can implement however we want. So you submit a bug report to the gcc maintainers: why didn't you warn me I was using non-portable code? And they close it too: how were we supposed to know it's not portable? We can't keep track of how everyone else implements the standard library.
Now what? No one did anything really wrong. Not you, not the gcc folks, nor the Visual Studio folks. Yet you still end up with un-portable code and are not a happy camper at this point. All else being equal, a good language standard will try to make this situation as unlikely as possible.
And even though I used an example of different compilers, it could just as well happen when you try to upgrade to a newer version of the same compiler, or even try to compile with different settings. For example: the function contains an assert statement to ensure it's being called with valid arguments. If you compile with assertions disabled, the assertion "disappears" and the function meets the rules for constexpr; if you enable assertions, then it doesn't meet them. (This is less likely these days now that the rules for constexpr are very generous, but was a bigger issue under the C++11 rules. But in principle the point remains even today.)
Lastly we get to the admittedly minor issue of error messages. In today's world, if I try to do something like stick in a cout statement in constexpr function, I get a nice simple error right away. In your world, we would have the same situation that we have with templates, deep stack-traces all the way to the very bottom of the implementation of output streams. Not fatal, but surely annoying.
This is a year and a half late, but I still hope it helps.
As Ben Voigt points out, compilers are already allowed to evaluate anything at compile time under the as-if rule.
What constexpr also does is lay out clear rules for expressions that can be used in places where a compile time constant is required. That means I can write code like this and know it will be portable:
constexpr int square(int x) { return x * x; }
...
int a[square(4)] = {};
...
Without the keyword and clear rules in the standard I'm not sure how you could specify this portably and provide useful diagnostics on things the programmer intended to be constexpr but don't meet the requirements.
The canonical way to read lines from a text file is:
std::fstream fs("/tmp/myfile.txt");
std::string line;
while (std::getline(line, fs)) {
doThingsWith(line);
}
(no, it is not while (!fs.eof()) { getline(line, fs); doThingsWith(line); }!)
This works beacuse std::getline returns the stream argument by reference, and because:
in C++03, streams convert to void*, via an operator void*() const in std::basic_ios, evaluating to the null pointer value when the fail error flag is set;
see [C++03: 27.4.4] & [C++03: 27.4.4.3/1]
in C++11, streams convert to bool, via an explicit operator bool() const in std::basic_ios, evaluating to false when the fail error flag is set
see [C++11: 27.5.5.1] & [C++11: 27.5.5.4/1]
In C++03 this mechanism means the following is possible:
std::cout << std::cout;
It correctly results in some arbitrary pointer value being output to the standard out stream.
However, despite operator void*() const having been removed in C++11, this also compiles and runs for me in GCC 4.7.0 in C++11 mode.
How is this still possible in C++11? Is there some other mechanism at work that I'm unaware of? Or is it simply an implementation "oddity"?
I'm reasonably certain this is not allowed/can't happen in a conforming implementation of C++11.
The problem, of course, is that right now, most implementations are working on conforming, but aren't there completely yet. At a guess, for many vendors, this particular update is a fairly low priority. It improves error checking, but does little (or nothing) to enable new techniques, add new features, improve run-time efficiency, etc. This lets the compiler catch the error you've cited (some_stream << some_other_stream) but doesn't really make a whole lot of difference otherwise.
If I were in charge of updating a standard library for C++11, I think this would be a fairly low priority. There are other changes that are probably as easy (if not easier) to incorporate, and likely to make a much bigger difference to most programmers.
To use one of the examples you gave, if I were in charge of updating the VC++ standard library to take advantage of the compiler features added in the November CTP, my top priority would probably be to add constructors to the standard container types to accept initialization_lists. These are fairly easy to add (I'd guess one person could probably add and test them in under a week) and make quite an obvious, visible difference in what a programmer can do.
As late as GCC 4.6.2, the libstdc++ code for basic_ios is evidently still C++03-like.
I'd simply put this down to "they haven't gotten around to it yet".
By contrast, the libc++ (LLVM's stdlib implementation) trunk already uses operator bool().
This was a missed mini-feature buried in a pre-existing header. There are probably lots of missing error of omission and commission in pre-2011 components.
Really, if anyone comes up with things like this in gcc then it would do a world of good to go to Bugzilla and make a bug report. It may be a low priority bug but if you start a paper trail
I'll go out on a limb and extend this idea to all the other C++ compilers: clang, Visual Studio,etc.
This will make C++ a better place.
P.S. I entered a bug in Bugzilla.