tag:blogger.com,1999:blog-7101933101966798446.post8182276492181723693..comments2024-03-28T10:33:06.910-07:00Comments on The View from Aristeia: Declare functions noexcept whenever possible?Scott Meyershttp://www.blogger.com/profile/05280964633768289328noreply@blogger.comBlogger59125tag:blogger.com,1999:blog-7101933101966798446.post-65741533196626684022014-04-15T19:21:46.969-07:002014-04-15T19:21:46.969-07:00@Arnaud: Ah, I see what you mean. Good point. I ag...@Arnaud: Ah, I see what you mean. Good point. I agree with you in principle, but I think it might be tricky to determine, in general, which functions are likely to be called inside destructors. Nevertheless, your point stands.Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-1625496792786854642014-04-15T10:26:46.699-07:002014-04-15T10:26:46.699-07:00Hi Scott,
my point was that good candidates for n...Hi Scott,<br /><br />my point was that good candidates for noexcept are functions which wants to be called from destructors (like unlock on mutexes for example).<br />The paragraph focuses on destructors themselves not on what can be safely called from them. IMHO cleanup(), unlock(), release(), close() and the likes should often be noexcept too.<br /><br />Hope this is clearer.<br /><br />Regards,<br /><br />ArnaudArnaudnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-873083807551234992014-04-14T08:54:18.034-07:002014-04-14T08:54:18.034-07:00@Alex Howlett: I just sent copies of our old corre...@Alex Howlett: I just sent copies of our old correspondence to the email address you were using in January 2007. If you don't get my message, send me your current email address (I'm at smeyers@aristeia.com), and I'll send the messages again.Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-20112714600419252692014-04-14T07:57:29.825-07:002014-04-14T07:57:29.825-07:00@Scott: Wow. That's awesome that you still ha...@Scott: Wow. That's awesome that you still have that correspondence. Do you think you could email it back to me? Among other things, I think it included some weird half-cooked notion of concepts that I'd like to read again.<br /><br />That's a really good point about swapping std::unique_ptrs. So while std::swap isn't nearly as expensive as it was before move semantics, it's still got some overhead.<br /><br />It's too bad that swap doesn't default to a member-wise swap the way copy/move operations default to member-wise copy/move. It would eliminate the need for many STL classes to have custom swaps.<br /><br />I hadn't read the Effective Effective Books post before. It's great advice. I like Item 5. I particularly like how three of my suggested Item titles violate it. =P<br /><br />-Only write copy functions for classes with non-copyable members.<br />+Use compiler-generated copy functions for classes with copyable members.<br /><br />-Avoid writing your own swap functions<br />+Prefer std::swap over rolling your own swap function.<br />(Not sure if this is still good advice given your example)<br /><br />-Avoid writing your own move functions<br />+Use compiler-generated move functions to implement move semantics.<br /><br />I also might revise the fourth one based on Item 9:<br /><br />-Keep your functions exception neutral.<br />+Prefer your functions to be exception neutral.<br /><br />This could simultaneously address the potential overuse of noexcept as well as the overuse of try/catch blocks that you see in the "Java++" style of coding. We all throw sometimes and even catch every once in a while, but these cases should be the exception (see what I did there?) rather than the rule. Similarly, I feel that making swap functions noexcept should be an exception to the rule.<br /><br />Item 13 has inspired me to take another look at your table of contents.Alex Howletthttps://www.blogger.com/profile/01043149701435832314noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-22376924980478866362014-04-14T06:59:12.896-07:002014-04-14T06:59:12.896-07:00@Vladimir: Interfaces don't have to be fully s...@Vladimir: Interfaces don't have to be fully specified by the standard. sizeof(std::vector<int>) is an unspecified part of std::vector<int>'s interface. Similarly, noexcept can go unspecified without making it any less part of a function's interface.<br /><br />That being said, I agree with you that some level of noexcept deduction might be desirable. Stroustrup makes the case for it here: www.stroustrup.com/N3202-noexcept.pdfAlex Howletthttps://www.blogger.com/profile/01043149701435832314noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-68079415322717718942014-04-13T21:46:46.110-07:002014-04-13T21:46:46.110-07:00@Alex: I looked up our email exchange regarding ex...@Alex: I looked up our email exchange regarding exceptions. It's from 2007. You have a very good memory :-) <br /><br />Regarding std::swap versus a class-specific swap, the general reason for writing your own is that it can be more efficient. Consider the cost of moving a std::unique_ptr. It can be thought of as two raw pointer assignments: one to copy the source to the destination, and one to set the source to null. (This is a simplification, because if we're doing a move assignment, and the destination isn't null, its pointee has to be destroyed, but we'll ignore that.) If I know I want to swap two std::unique_ptrs, I expect to pay three raw pointer assignments: dest to temp, source to dest, temp to source. If I put a std::unique_ptr into a class and let the class generate the move operations, the cost of moving a class object is the same as moving the std::unique_ptr it contains: two raw pointer assignments. But if I use std::swap on a class object, I perform three move operations on that object (dest to temp, source to dest, temp to source), and, at two raw pointer assignments per object move operation, that totals six raw pointer assignments. On its face, that's twice as expensive, though compilers may be able to optimize some or all of the extra cost away via dead assignment analysis. This is why the standard containers write their own swap routines. Behaviorally, std::swap is fine. In terms of performance, it may not be optimal.<br /><br />Regarding your preference for guidelines that can be put on a checklist, I fully agree with you (see Item 3 <a href="http://scottmeyers.blogspot.com/2013/01/effective-effective-books.html" rel="nofollow">here</a>), and I thank you for reminding me of this. I'll work harder to eliminate guidelines that begin with "Consider" and "Understand".Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-19731836674166277882014-04-13T21:00:03.217-07:002014-04-13T21:00:03.217-07:00@Vladimir: Whether a function is noexcept is certa...@Vladimir: Whether a function is noexcept is certainly part of its interface, because client code may make decisions that depend on whether a function is noexcept. The most obvious example is the use of std::move_if_noexcept, but the fundamental question of whether a function is guaranteed to return to its caller (short of extralinguistic events such as somebody killing a process) is affected by whether functions it calls are noexcept. <br /><br />Note that if we had noexcept(auto), callers could still write code that depends on whether a function is noexcept(true), because they could use noexcept expressions.<br /><br />As I noted in another comment, there are a number of noexcept functions in the standard library that have nothing to do with moving or swapping, and I don't think they can be characterized as being at module boundaries. Examples include std::move and std::forward. <br /><br />I am unaware of any code that would be broken if a function that was noexcept(false) became noexcept(true), so introducing noexcept(auto) as a default would not, as far as I can tell (with only minimal thinking about it), be a breaking change. This is true for both inline and non-inline functions, I think.Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-75481321054915325062014-04-13T19:51:39.974-07:002014-04-13T19:51:39.974-07:00@Arnaud: I'd hope that the material on page 8,...@Arnaud: I'd hope that the material on page 8, lines 21-25, and page 9, lines 1-7, would make clear that destructors and operators delete should normally be noexcept. No?<br /><br />ScottScott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-89883030585476619062014-04-13T14:07:39.017-07:002014-04-13T14:07:39.017-07:00@Alex Howlett
>>But noexcept(auto) as descr...@Alex Howlett<br /><br />>>But noexcept(auto) as described in n3207 does *not* denote exception neutrality.<br />Sorry, "denote" is a wrong choice of a word.<br />noexcept(auto) is an appropriate/natural exception specification for an exception neutral function.<br /><br />>>If noexcept(auto) denoted exception neutrality, the difference between noexcept(false) and noexcept(auto) would then be that noexcept(auto) functions would fail to compile if they tried to throw/catch their own exceptions, correct? Defaulting inline function to this form of noexcept(auto) would therefore be a breaking change for any inline function that contained a throw statement or a try/catch block.<br />No, no, no. My mistake with "denote" again. noexcept(auto) behaves as described in n3207.<br /><br />>>Any function deduced to be noexcept(false) would be exception neutral if and only if its body contained no throws or try/catch blocks.<br />I do not understand. The deduction process itself (see n3207) is independent from presence of try/catch blocks and therefore exception neutrality.<br /><br />>>The exception-neutrality of a function is not part of its interface. Both noexcept(false) and (user-specified) noexcept(true) functions can be exception neutral or not.<br />Correct.<br /><br />>>The noexcept status of a function is indeed part of its interface because the information is used by the callers.<br />And that is the main question and my key point. Not everything used by the callers is a part of interface. sizeof(std::vector<int>) can be used too, but the concrete number is not specified and is not a part of vector's interface. And I see the similar situation with exception specifications. I'll describe it again by summarizing my previous words:<br />1) noexcept(auto) (or its imitation) is a natural exception specification for exception neutral functions (i.e. most of functions)<br />2) concrete exception specification deduced by noexcept(auto) is an implementation detail and not part of an interface. It is unspecified and can be changed (even between Debug/Release modes, see Library Issue 2319 about debug STL in Visual C++ as a related concrete example).<br />3) although unspecified, it can be productively used, e.g. by std::move_if_noexcept and similar switching functions<br />4) conclusion: for most of functions their natural exception specification is not a part of their interface<br /><br />Does that make sense? :)Vladimirnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-16345003286104708792014-04-13T13:01:06.712-07:002014-04-13T13:01:06.712-07:00Hi Scott,
this chapter contains very strong advic...Hi Scott,<br /><br />this chapter contains very strong advice I would say. The only thing I would add is that there is another group of functions which almost always deserves noexcept:<br /><b>Cleanup functions</b>.<br />If we want exception safety, we need to put all the cleanup code in destructors (RAII). And as a result, those functions should really be noexcept.<br />What do you think about this?<br /><br />Regards,<br /><br />ArnaudArnaudnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-53419304355209452932014-04-12T20:29:00.973-07:002014-04-12T20:29:00.973-07:00@Vladimir, thanks for the clarification on "e...@Vladimir, thanks for the clarification on "exception neutral." I wasn't drawing the distinction between functions that allow any exception to be emitted and functions that might have throw statements or try/catch blocks in their bodies.<br /><br />If noexcept(auto) denoted exception neutrality, the difference between noexcept(false) and noexcept(auto) would then be that noexcept(auto) functions would fail to compile if they tried to throw/catch their own exceptions, correct? Defaulting inline function to this form of noexcept(auto) would therefore be a breaking change for any inline function that contained a throw statement or a try/catch block.<br /><br />But noexcept(auto) as described in n3207 does *not* denote exception neutrality. Any function deduced to be noexcept(false) would be exception neutral if and only if its body contained no throws or try/catch blocks.<br /><br />The exception-neutrality of a function is not part of its interface. Both noexcept(false) and (user-specified) noexcept(true) functions can be exception neutral or not.<br /><br />The noexcept status of a function is indeed part of its interface because the information is used by the callers.<br /><br />Does that make sense?Alex Howletthttps://www.blogger.com/profile/01043149701435832314noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-64093295360160811122014-04-12T12:08:42.272-07:002014-04-12T12:08:42.272-07:00@Alex Howlett
>>we're using the term &qu...@Alex Howlett<br />>>we're using the term "exception neutral" to describe functions that have no restrictions on the exceptions they're allowed to emit.<br />I mean functions that just propagate all exceptions thrown inside of them and don't specifically deal with catching them (as in the Scott Meyers' draft and in the discussion above).<br />Exception neutral function can throw if it contains potentially throwing code and can't throw if it doesn't, i.e. its exception specification can be auto deduced.<br /><br />>>Could you clarify what you mean by noexcept(auto)?<br />>>Are you talking about this?<br />>>http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3207.htm<br />Yes, exception specification deduced from function's body (very similar to auto return type).<br /><br />>>I don't think there's any general way to determine whether a function might emit an exception without running into the halting problem.<br />It is already done (approximately, of course) for special member functions (N3937 15.4 Exception specifications [except.spec] 10). All of them have auto deduced exception specification by default.Vladimirnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-14745793782265414652014-04-12T07:19:42.505-07:002014-04-12T07:19:42.505-07:00@Vladimir, we're using the term "exceptio...@Vladimir, we're using the term "exception neutral" to describe functions that have no restrictions on the exceptions they're allowed to emit.<br /><br />The only functions that aren't exception neutral by default when you declare them are destructors and operator delete overloads. noexcept(false) explicitly denotes exception-neutral functions.<br /><br />Could you clarify what you mean by noexcept(auto)?<br /><br />Are you talking about this?<br /><br />http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3207.htm<br /><br />I don't think there's any general way to determine whether a function might emit an exception without running into the halting problem. noexcept guarantees that a function won't throw by saying, "Hey, callers! If I try to throw an exception, I'll crash the program for you so you never have to deal with it."Alex Howletthttps://www.blogger.com/profile/01043149701435832314noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-77696865789458861352014-04-12T02:53:42.891-07:002014-04-12T02:53:42.891-07:00I wonder, is noexcept really part of a function in...I wonder, is noexcept really part of a function interface?<br />I'm interested in opinions of more experienced people.<br /><br />Assume for a second, that we have exactly three kinds of exception specifications:<br />noexcept(true) - function can't throw exceptions,<br />noexcept(false) - function can throw exceptions,<br />noexcept(auto) - it is unspecified (but known at compile time) if function can or can't throw exceptions.<br />(Further I'm talking only about functions for which noexcept(auto) is applicable - i.e. inline functions mostly).<br /><br />Then explicit noexcept(true) and noexcept(false) are definitely parts of an interface, but noexcept(auto) is not. It still turns into noexcept(true) or noexcept(false) during compilation, but user can't actively rely on it.<br /><br />And why is noexcept(auto) important? Because it denotes exception neutral functions. And, as was mentioned, most functions are exception neutral, even move operations and swap (just look at the standard library), even compiler-generated destructors.<br />(Another story, that you may static_assert noexceptness of your move/swap, just to be sure (Gonzalo BG).)<br />(The non-exception neutral functions are functions at module boundaries, and user-written destructors, and may be something else.)<br /><br />I.e. for most of functions their noexcept specification is not part of their interface.<br /><br />Currently absence of exception specification on inline functions can be viewed as noexcept(auto), which always turns into noexcept(false).<br />(Except for the standard library, which can freely add noexcept(true) to functions, not required to have exception specification by the standard).<br />So, I also wonder, if excepion specificatons of all inline non-noexcept functions (including lambdas!) are suddenly turned into noexcept(auto), would it be a breaking change?Vladimirnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-48286928460273110282014-04-11T08:22:53.684-07:002014-04-11T08:22:53.684-07:00Scott I think I may have emailed you a rant severa...Scott I think I may have emailed you a rant several years ago about exception guarantees. In it, I argued what you're arguing now: that a function with a non-throwing specification could be treated by the caller as offering the nothrow guarantee. Anyway, I wrote such a rant and emailed it to someone. I still agree with it and I agree with you now.<br /><br />But here's my question: How important is the nothrow guarantee?<br /><br />The nothrow guarantee is critical for certain standard library facilities that are guaranteed not to crash (i.e. code without bugs). But even the STL only applies noexcept judiciously so as to allow debug out of range exceptions on containers and exceptions emitted from client code that's called by the library.<br /><br />The default behavior of a propagated exception is that your program terminates. This is the *same* behavior offered by noexcept. But when you apply noexcept, you're forbidding yourself from modifying that default behavior to something potentially more useful.<br /><br />The beauty of C++'s exception mechanism is that it allows most functions to completely ignore error handling. Exceptions make for cleaner, more efficient code. Personally, I like to put a function try block around my main function so that before I terminate, I catch and log any exceptions. Adding noexcepts to code is like adding try/catch blocks that call std::terminate(). If there happens to be a handler out there somewhere, noexcept prevents the exceptions from reaching it. <br /><br />In the real world, it's very difficult to know whether a function will never emit and exception and never be *modified* to emit an exception. In the real world, there are good reasons (including debugging) why you might want to modify a function to make it throw. In the real world, noexcept makes code more brittle and harder to work with. It breaks the C++ exception mechanism. But even so, noexcept might be worth it sometimes.<br /><br />So, when is noexcept worth it?<br /><br />On page 6, you say, "As a general rule, the only time it makes sense to actively search for a 20 noexcept algorithm is when you’re implementing the move functions or swap."<br /><br />A lot of the commenters here seem to agree with you.<br /><br />But why are we writing our own move functions? Assuming we properly encapsulate our resources in smart pointers, when would we ever have to use anything but the compiler-generated move functions for our classes?<br /><br />Also, why are we writing our own swap functions? What's wrong with std::swap? As long as our classes are copyable or moveable, std::swap should work fine.<br /><br />Something else to keep in mind is that const can be checked at compile time while noexcept cannot. If you use either one enough, you'll eventually make a mistake. If you make a mistake with const, the compiler will catch you. But playing with noexcept is a dangerous game.<br /><br />I agree with Seth that applying noexcept other than in very specific situations is premature optimization. I also agree with the bulk of what Gonzalo BG said. And I really liked akrzemi1's linked article. I'd be curious to know your thoughts on what he says in there.<br /><br />Regarding the title, I like items that can easily go on a checklist. I'm not a fan of titles that begin with "Consider" or "Understand": "Did I consider this? I think so..." "Do I understand? I'm not sure..."<br /><br />I wouldn't mind seeing the following items:<br /><br />"Only write copy functions for classes with non-copyable members."<br /><br />"Avoid writing your own swap functions."<br /><br />"Avoid writing your own move functions."<br /><br />"Keep your functions exception-neutral."<br /><br />These are easy rules to follow: "Did I avoid writing swap functions? Yes." "Did I avoid writing move operations? Yes." "Did I keep my functions exception-neutral? Yes."<br />Alex Howletthttps://www.blogger.com/profile/01043149701435832314noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-75685387469730275812014-04-10T10:07:10.695-07:002014-04-10T10:07:10.695-07:00@Greg: I think it's important to bear in mind ...@Greg: I think it's important to bear in mind that, if you follow the examples in the language and standard library, more than move and swap functions benefit from noexcept. Destructors and operators delete are noexcept by default, and among the standard library functions that are unconditionally noexcept are std::move, std::forward, various functions in std::numeric_limits, abort, atexit/at_quick_exit/quick_exit, various functions in std::type_info... you can search the standard as easily as I can.<br /><br />Regarding your last comment, noexcept is enforced at runtime. If you call a noexcept function, the caller will never see an exception from that function. Callers can thus use noexcept in their reasoning about the exception behavior of the functions they call. What you call the "std::terminate hail mary" is the basis of this guarantee.Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-85460683370662023512014-04-10T00:30:22.988-07:002014-04-10T00:30:22.988-07:00Also re: "It also gives callers a guarantee t...Also re: "It also gives callers a guarantee that they can use in determining what kind of exception-safety guarantee they will offer." I'm not sure if this is really that strong a point, since my understanding is that with unchecked exceptions, it is simply the "word" of the original author, akin to a "structured comment" or something, not a statically or dynamically enforced contract. (std::terminate hail mary aside)<br /><br />For example, if the author of the function you are calling just likely put it there because they heard you should use it "whenever you can" from somewhere it's an untrustworthy guarantee indeed :)Greghttps://www.blogger.com/profile/16138954675664901193noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-15419960098698745722014-04-10T00:12:12.482-07:002014-04-10T00:12:12.482-07:00FWIW as per my advice above i'd propose a titl...FWIW as per my advice above i'd propose a title along the lines of "Consider noexcept on move operations and swap"Greghttps://www.blogger.com/profile/16138954675664901193noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-83483886064102806932014-04-09T23:58:31.665-07:002014-04-09T23:58:31.665-07:00The current wording of "Declare functions noe...The current wording of "Declare functions noexcept whenever possible" feels to me like the type of thing that could morph into cargo cultism due to a perceived free lunch.<br /><br />It seems to be much more nuanced than the title suggests, as declaring a function noexcept is a very weighty decision to make. It can't be undone easily, and expresses some very hard-to-say-with-certainty things about a function and its callees for *eternity*. This type of scenario could lead to a very terrible situation: C++ code of the future riddled with poorly conceived "noexcept"'s everywhere due to a shallow impression that "Effective Modern C++ says noexcept makes code go faster!" The potential result: the keyword at becomes so misused that not only is it meaningless but compiler writers *can't* optimize for it.<br /><br />It seems the clear and obvious big win that exists *now* for noexcept is the performance gains when declared properly on move/swap. This also is conceptually easy to understand and provide positive and negative examples for when noexcept is appropriate. Why not focus on this case alone for the Item? <br /><br />The compiler optimization point seems to be a bit speculative, perhaps that can be included in a second edition if and when compiler writers manage to leverage it in a way that is well understood and has significant advantages. As stated in the item now, you can always add "noexcept" later, so it seems the book can always expand its advice in a way that further proliferates it's use properly.<br /><br />It seems worth not opening pandora's box right now of "noexcept everywhere" if the use-cases that warrant the "whenever possible" choice of words seem minimal and perhaps even non-existent.Greghttps://www.blogger.com/profile/16138954675664901193noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-17748806407212670022014-04-06T13:58:14.427-07:002014-04-06T13:58:14.427-07:00@Seth: Seeing benchmark results would certainly be...@Seth: Seeing benchmark results would certainly be interesting, but given how recently noexcept was added to the language, it's not clear how many optimizations dependent on that feature we should expect to be implemented in current compilers.Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-73299509088887546302014-04-06T12:33:56.636-07:002014-04-06T12:33:56.636-07:00I didn't mean to say that applying noexcept to...I didn't mean to say that applying noexcept to move/swap should _always_ be done, or done without thinking. Only that because the effects there are much better understood and apply across platforms I don't feel like I need to wait for profiling to know if noexcept is good there or not.<br /><br />I haven't found any benchmarking of noexcept so I'm thinking about trying to do so myself. I think some real results would really help me understand the relative importance of those optimizations.<br /><br />---<br /><br />Jon Kalb's talk makes the same point in his presentation about how important noexcept is for users so that they can provide the exception guarantees they want, but still concludes that move/swap is the critical functionality.Sethnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-26746749605670571152014-04-05T22:19:03.682-07:002014-04-05T22:19:03.682-07:00@Seth: In my view, applying noexcept to move opera...@Seth: In my view, applying noexcept to move operations without thinking about it is at least as big an error as failing to apply it when you can. noexcept move operations are desirable, but they're not required, and they may not always be possible. For example, std::array's move operation can't be unconditionally noexcept, and the move operations for the other STL containers are not required to be (and, last I checked, at least some are not in some implementations). <br /><br />Regarding exception specs and code gen, it's certainly the case that if you have an exception spec, you have to generate code to check to make sure it's not violated. Exception specs have a cost. But my understanding is that in many cases the cost is more than compensated for by other optimization opportunities. Again, however, I am not a compiler writer.<br /><br />It's worth noting, now that I think about it, that the implications of declaring a function noexcept extend beyond performance. It also gives callers a guarantee that they can use in determining what kind of exception-safety guarantee they will offer. Which kind of drives me back to a "whenever possible" perspective, because you'd like to give callers as many tools as you can for them to offer strong exception safety guarantees, no?<br /><br />ScottScott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-40894867211694200232014-04-05T19:22:56.521-07:002014-04-05T19:22:56.521-07:00Yeah, after the second interpretation occurred to ...Yeah, after the second interpretation occurred to me I suspected that it was closer to your intent all along. I'd been reading the title more like the first interpretation.<br /><br />I'm wavering back and forth a bit on "Consider noexcept for performance-sensitive functions."<br /><br />The mention of performance I think indicates that applying noexcept too early could be premature optimization which I think would cut down on the mistake I made in understanding the first title. At the same time I feel like _not_ applying noexcept to move/swap early would, in some cases, qualify as 'premature pessimization '. This second point is really the only issue I have with the title "Consider noexcept for performance-sensitive functions."<br /><br />Perhaps it's that I have a good understanding of the performance gains related to enabling move semantics in containers and algorithms (and if I didn't before reading this chapter then I would afterwards), but I'm much more vague on the benefits of the compiler optimizations enabled by noexcept. I've seen some examples of better codegen but I don't have a good intuition for when they will kick in or how large the effect is. Different compilers seem to provide different levels of support (msvc seems to do more than gcc, which seems to do slightly more than clang), and I've even seen at least one comment by compiler devs that exception specifications could hurt codegen.<br /><br />So I'm comfortable deciding up front if noexcept is appropriate on a move/swap function, and everywhere else I wouldn't want to do it until I could profile the results.<br /><br />I suppose one of the reasons I liked "Know where to apply noexcept" (or "Know when to apply noexcept"?) was that that title doesn't try to squeeze in any indication of when/where that was; Any such shortened explanation seems doomed apparently contradict some part of the full explanation, with all its subtleties.Sethnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-2292383908071060742014-04-04T09:30:11.851-07:002014-04-04T09:30:11.851-07:00@Seth: I had the second interpretation in mind, bu...@Seth: I had the second interpretation in mind, but, as I wrote in another comment, it's apparent that the current Item title doesn't convey what I'd like it to, so I'm planning to change it. What do you you (and others) think of "Consider noexcept for performance-sensitive functions"?Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-86061433651343996352014-04-04T03:32:40.816-07:002014-04-04T03:32:40.816-07:00Hi Scott,
I see your point, I hope you see my po...Hi Scott, <br /><br />I see your point, I hope you see my points too.<br /><br />I checked your example and gcc 4.9 removes the dead code when the functions are noexcept, still this is IMO very low hanging fruit, and since noexcept is part of a function interface, why are you wrapping noexcept code in a try-catch in the first place?<br /><br />Lets check Pros/Cons of noexcept. Starting with cons:<br /><br />1) Once a function is made noexcept, you can't go back (API breakage).<br /><br />2) To use it correctly you have to answer the question: "Am I sure that nothing in the context of this noexcept function call will throw?" This question is really hard! How should I know? Trying to answer it for every function you write is a huge mental burden! <br /><br />3) Failing to answer 2 correctly inserts paths to std::terminate all over your code. Testing might not trigger these, but demoing the project to your client certainly will. Had you let exceptions propagate, some higher level code might already be in place to handle them.<br /><br />4) It adds some verbosity and complexity to function declarations. In generic code, it adds a lot of verbosity and complexity to function declarations. <br /><br />5) It can mask real problems: e.g. if instead of making move construction/assignment/swap noexcept you statically assert that they are, and they are not, there might be nothing wrong with your type, but you might be using a data member of some type with a throwing move assignment or what have you. Fixing that instead of making a function noexcept might allow algorithmic optimizations for a lot of types, not just your single type. <br /><br />Pros:<br /><br />1) Noexcept move constructor/move assignment/swap do enable algorithmic optimizations that consistently deliver much better performance.<br /><br />2) There are some situations in which noexcept gives you some extra type safety (e.g. the exception specification of virtual functions is checked at compile-time for consistency). In this situations, noexcept is better than nothing.<br /><br />3) If all the code within a try-block is noexcept, and you write the try-block anyways, then, the catch block will be removed by modern compilers which might allow some other optimizations.<br /><br />So those are my 5 cons vs 3 pros (I know you have others, I hope these start a discussion). From the pros: 1) and 2) are worth it, I think one should strive for using noexcept in these cases. From the cons: 1), 2), and 5) are really bad, 3) is just a consequence of 2), but it happens if people use noexcept blindly due to promises of better performance. 4) is a bit subjective, but it is a real problem in generic code. <br /><br />So do the pros out weight the cons? <br /><br />Not in the general case, only for the situations explained in pros 1) and 2), which are a small subset of all the places in which one could use noexcept. <br /><br />Why is this the case?<br /><br />IMO because noexcept is just too hard to use correctly (see cons 2). In C++03 the right mindset for writing exception-safe code was "everything can and will throw". Stick to this, and writing exception-safe code is actually easy. However, fulfilling cons 2) is hard. There are some few cases in which the trouble is worth it: e.g. for critical operations (move constr./assignmt/swap) I let the compiler answer it for me (with static_assert) and make sure that it asserts to noexcept(true). But for the rest of my code? The cons out weight the pros by far.<br /><br />Give me some help:<br />- transitivity (statically type-check that noexcept code only calls other noexcept code),<br />- an unsafe noexcept_cast to deal with context in which i know that a throwing function won't throw (and make those explicit), and<br />- noexcept(auto) to easily write generic code that propagates noexcept correctly,<br />and I will gladly reconsider.Gonzalo BGhttp://gnzlbg.github.ionoreply@blogger.com