For some functions, being noexceptis so important, they're that way by default. In C++98, it was considered bad style to permit the memory deallocation functions (i.e., operator deleteand operator delete[]) and destructors to emit exceptions, and in C++11, this style rule has been all but upgraded to a language rule. By default, all memory deallocation functions and all destructors — both user-defined and compiler-generated — are implicitly noexcept. There's thus no need to declare them noexcept. (Doing so doesn't hurt anything, it's just unconventional.) The only time a destructor is not implicitly noexceptis when a data member of the class (including inherited members and those contained inside other data members) is of a type that expressly states that its destructor may emit exceptions (e.g., declares it “ noexcept(false)”). Such destructors are uncommon. There are none in the Standard Library, and if the destructor for an object being used by the Standard Library (e.g., because it's in a container or was passed to an algorithm) emits an exception, the behavior of the program is undefined.
It's worth noting that some library interface designers distinguish functions with wide contracts from those with narrow contracts . A function with a wide contract has no preconditions. Such a function may be called regardless of the state of the program, and it imposes no constraints on the arguments that callers pass it. [5] “Regardless of the state of the program” and “no constraints” doesn't legitimize programs whose behavior is already undefined. For example, std::vector::size has a wide contract, but that doesn't require that it behave reasonably if you invoke it on a random chunk of memory that you've cast to a std::vector . The result of the cast is undefined, so there are no behavioral guarantees for the program containing the cast.
Functions with wide contracts never exhibit undefined behavior.
Functions without wide contracts have narrow contracts. For such functions, if a precondition is violated, results are undefined.
If you're writing a function with a wide contract and you know it won't emit exceptions, following the advice of this Item and declaring it noexceptis easy. For functions with narrow contracts, the situation is trickier. For example, suppose you're writing a function f taking a std::stringparameter, and suppose f's natural implementation never yields an exception. That suggests that f should be declared noexcept.
Now suppose that fhas a precondition: the length of its std::stringparameter doesn't exceed 32 characters. If f were to be called with a std::stringwhose length is greater than 32, behavior would be undefined, because a precondition violation by definition results in undefined behavior. f is under no obligation to check this precondition, because functions may assume that their preconditions are satisfied. (Callers are responsible for ensuring that such assumptions are valid.) Even with a precondition, then, declaring f noexceptseems appropriate:
void f(const std::string& s) noexcept; // precondition:
// s.length() <= 32
But suppose that f's implementer chooses to check for precondition violations. Checking isn't required, but it's also not forbidden, and checking the precondition could be useful, e.g., during system testing. Debugging an exception that's been thrown is generally easier than trying to track down the cause of undefined behavior. But how should a precondition violation be reported such that a test harness or a client error handler could detect it? A straightforward approach would be to throw a “precondition was violated” exception, but if f is declared noexcept, that would be impossible; throwing an exception would lead to program termination. For this reason, library designers who distinguish wide from narrow contracts generally reserve noexceptfor functions with wide contracts.
As a final point, let me elaborate on my earlier observation that compilers typically offer no help in identifying inconsistencies between function implementations and their exception specifications. Consider this code, which is perfectly legal:
void setup(); // functions defined elsewhere
void cleanup();
void doWork() noexcept{
setup(); // set up work to be done
… // do the actual work
cleanup(); // perform cleanup actions
}
Here, doWorkis declared noexcept, even though it calls the non- noexceptfunctions setupand cleanup. This seems contradictory, but it could be that setupand cleanupdocument that they never emit exceptions, even though they're not declared that way. There could be good reasons for their non- noexceptdeclarations. For example, they might be part of a library written in C. (Even functions from the C Standard Library that have been moved into the stdnamespace lack exception specifications, e.g., std::strlenisn't declared noexcept.) Or they could be part of a C++98 library that decided not to use C++98 exception specifications and hasn't yet been revised for C++11.
Because there are legitimate reasons for noexceptfunctions to rely on code lacking the noexceptguarantee, C++ permits such code, and compilers generally don't issue warnings about it.
Things to Remember
• noexceptis part of a function's interface, and that means that callers may depend on it.
• noexceptfunctions are more optimizable than non -noexceptfunctions.
• noexceptis particularly valuable for the move operations, swap, memory deallocation functions, and destructors.
• Most functions are exception-neutral rather than noexcept.
Item 15: Use constexprwhenever possible.
If there were an award for the most confusing new word in C++11, constexprwould probably win it. When applied to objects, it's essentially a beefed-up form of const, but when applied to functions, it has a quite different meaning. Cutting through the confusion is worth the trouble, because when constexprcorresponds to what you want to express, you definitely want to use it.
Conceptually, constexprindicates a value that's not only constant, it's known during compilation. The concept is only part of the story, though, because when constexpris applied to functions, things are more nuanced than this suggests. Lest I ruin the surprise ending, for now I'll just say that you can't assume that the results of constexprfunctions are const, nor can you take for granted that their values are known during compilation. Perhaps most intriguingly, these things are features . It's good that constexprfunctions need not produce results that are constor known during compilation!
But let's begin with constexprobjects. Such objects are, in fact, const, and they do, in fact, have values that are known at compile time. (Technically, their values are determined during translation , and translation consists not just of compilation but also of linking. Unless you write compilers or linkers for C++, however, this has no effect on you, so you can blithely program as if the values of constexprobjects were determined during compilation.)
Читать дальше