Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ noexcept for a function not throwing exceptions, but can cause a memory failure

Tags:

c++

noexcept

For example, it's pretty common to have two separate ways to access elements of a private array, overloading the array subscripting operator, or defining at:

T& operator[](size_t i) { return v[i]; }
T const& operator[](size_t i) const { return v[i]; }

T& at(size_t i)
{
     if (i >= length)
         throw out_of_range("You shall not pass!");

     return v[i];
}

T const& at(size_t i) const
{
     if (i >= length)
         throw out_of_range("You shall not pass!");

     return v[i];
}

The at version can throw an exception, but the array subscripting operator can't.

My question is, altough the operator[] doesn't throw an exception, can it be marked as noexcept even knowing it can raise a SIGSEGV signal, or is it only a bad practice?

I want to point out a signal (as SIGSEGV) is not an exception. Being literal interpreting the noexcept meaning, a noexcept function is one that claims it won't throw exceptions. It says nothing about anything else (including signals).

But, a noexcept function has more meaning than that, at least for clientes of my code. noexcept also implicitly says the function is safe and it will finish its execution without computational errors.

So, is it inappropiate to mark noexcept a function which isn't safe?

like image 335
Peregring-lk Avatar asked May 13 '15 18:05

Peregring-lk


2 Answers

This is a tough question and brings up some of the key issues covered in noexcept — what for? from Andrzej's C++ blog which says, I will attempt to quote the minimum here (emphasis mine):

In this post I would like to share my observation on where using noexcept really adds value. It is less often than what one might expect, and it does not have that much to do with throwing or not throwing exceptions. The conclusion surprises me a bit, and I hesitate to present it because it is counter to the advice I hear from people I consider authorities on the subject.

and:

Given this negative attitude to noexcept, can it be considered useful at all? Yes. The noexcept feature was introduced very late into C++11 to address one particular issue with move semantics. It has been described here by Douglas Gregor and David Abrahams.

and then he goes on to given an unusual move assignment definition and argues that what we really want to convey is not that it does not throw exceptions but that it does not fail, but that is very difficult problem but it is out real intent:

[...]This is because the information that noexcept really is intended to convey is that the function never fails; not that it never throws! We can see above that a function can fail but still not throw, but it still qualifies for noexcept(false). Perhaps the keyword should have been called nofail. The never-fail guarantee cannot be checked by the compiler (much like any other failure-safety guarantee), therefore the only thing we can do is to declare it.

This is part of a more general observation, that what we are interested in is really failure safety in program components rather than exception safety. No matter if you use exceptions, error return values, errno or what

ever else, the reasoning about basic (no leak, invariant preserved), strong (commit or rollback) and never-fail guarantee should still hold.

So if we take a similar position then, it would seem the answer is no, if not appropriate to use noexcept and that seems to be where you are leaning. I don't think it is clear cut answer.

He also notes proposal N3248: noexcept Prevents Library Validation. Which in turn was the basis for N3279: Conservative use of noexcept in the Library. This paper defines narrow and wide contracts as did N3248:

Wide Contracts

A wide contract for a function or operation does not specify any undefined behavior. Such a contract has no preconditions: A function with a wide contract places no additional runtime constraints on its arguments, on any object state, nor on any external global state. Examples of functions having wide contracts would be vector::begin() and vector::at(size_type) . Examples of functions not having a wide contract would be vector::front() and vector::operator[](size_type) .

Narrow Contracts

A narrow contract is a contract which is not wide. Narrow contracts for a functions or operations result in undefined behavior when called in a manner that violates the documented contract. Such a contract specifies at least one precondition involving its arguments, object state, or some external global state, such as the initialization of a static object. Good examples of standard functions with narrow contracts are vector::front() and vector::operator[](size_type) .

and recommends:

Each library function having a wide contract, that the LWG agree cannot throw, should be marked as unconditionally noexcept.

and implies that functions with narrow contracts should not be noexcept, which is backed up by LWG issue 2337 which says:

[...]These design considerations override our general policy against noexcept for narrow-contract functions. [...]

So if we want to be conservative and follow standard library practice then it would seem since operator[] does not have a wide contract it should not be marked noexcept.

like image 101
Shafik Yaghmour Avatar answered Oct 06 '22 06:10

Shafik Yaghmour


The fact: either you use noexcept or not, the function can still throw SIGSEGV.

Now, I think the answer is subjective and depends on one's personal perspective. What would you expect from a noexcept function? Would you expect that it never fails (even if noexcept does not offer such guarantee)? How will you handle a non-safe function in comparison with a "safe" function? Will it be a big difference in the project? If your answer is yes, than you perhaps should not use noexcept.

On the other hand, by not using it (when you know for certain it cannot throw exceptions) would make another programmer worry about providing a try-clause when calling this function.

From Bjarne Stroustroup "The C++ Programming Language", 4th edition:

Declaring a function noexcept can be most valuable for a programmer reasoning about a program and for a compiler optimizing a program. The programmer need not worry about providing try-clauses (for dealing with failures in a noexcept function) and an optimizer need not worry about control paths from exception handling.

So, is it inappropiate to mark noexcept a function which isn't safe?

My personal point of view is No, it is not bad practice or "immoral" to use it, all the more so as it can have some benefits.

like image 37
Violeta Marin Avatar answered Oct 06 '22 07:10

Violeta Marin