noexcept-correctenss

252 views
Skip to first unread message

federico...@gmail.com

unread,
May 7, 2018, 4:17:38 PM5/7/18
to ISO C++ Standard - Future Proposals
A nearly unique property of the C++ language are the destructors.

Handling failures is hard, but when executing a destructor it is twice as hard since we should not fail.
How are we supposed to handle a failure when we might already failed at something?

Another thing that I like a lot are the exception guarantees of the standard library.
(No-throw guarantee, Strong exception safety, Basic exception safety and No exception safety)

Implementing a strong exception-safe routine (or even no-throw) is (almost) easy if we have some `noexcept` functions at disposal.

The underlying problem is assuring that all functions (event those called implicitly) are `noexcept` all the time.

If one of the called function changes, it might break silently our code.

Suppose that we have something like

````
void foo(const G& g) noexcept{
    bar(g);
    g.baz();
}
`````

and we want to be sure that it will remain `noexcept` (`bar`, `baz`, and `G`could change in the future)

We need to write something like

````
void foo(const G& g) noexcept(strong){// compiles only if all called operations are noexcept(true)
    static_assert(std::is_nothrow_copy_constructible<G>::value, "ERROR"); // Because bar might not take a const reference but a copy(!)
    static_assert(noexcept(bar(), "ERROR");;
    bar(g);
    static_assert(noexcept(std::declval<G>().baz(), "ERROR");
    g.baz();
}
````

Which is barely readable, and error prone, since it easy to forget to `static_assert` some operation. (implicit conversion, copy constructors when calling other functions, ... I did not find a `std::is_nothrow_destructible` in case `bar` make a copy...)


I would like to propose a syntax like:

````
void foo(const G& g) noexcept(strong){ // compiles only if and only if all called operations are noexcept(true), it should also take copy elision into account in case `foo` returns something
    bar(g);
    g.baz();
}
````

and of course `noexcept(strong)` should imply `noexcept(true)` (`strong` is just a placeholder, it could be another keyword/identifier, an enum value, ...)



Since destructors should not throw, I see more and more people writing code like this (just to be on he safe side)

````
~myclass(){
    try{
        foo();
    }catch(...){}
}
````

which clutters the code, since this code does not tell me if foo can really fail (and in most cases, `foo` did only free memory, or call other functions that did not throw).

It would be much better to be able to write

````
~myclass() noexcept(strong){
    foo();
}
````

And be sure that it will not throw, and that there are no errors to ignore.

I hope that something like `noexcept(strong)` could help to avoid those constructs, and making writing exception-safe code easier.
It seems to me like a great addition to the language, similar to const-correctness.


Since it would not be possible to implement it as a library change, I wanted to gather some ideas, get some feedback, before even trying to write a paper.


Are there any obvious downsides/issues that I missed?
Was something similar already proposed in the past and maybe rejected?



Thank you in advance for your feedback

Federico

federico...@gmail.com

unread,
May 10, 2018, 3:25:55 PM5/10/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
I've played a little more with the idea of `noexcept`-correctness, so here my thoughts.

Suppose that we have following line function:


----
// header file foo.hpp
void foo(const G& g) noexcept;

// source file foo.cpp

void foo(const G& g) noexcept {
    bar(g);
    g.baz();
}
----

I've marked `foo` as `noexcept`, because when I wrote it, `bar` and `baz` where `noexcept` too, and `bar` took `g` as constant reference.

When code changes happen, some functions that could not fail before, now can.
Failing, in this context, does not mean that we get in a non-recoverable state, but simply that we need to handle it.

In it's current form our whole programs could suffer for our `noexcept` specification, because of a possibly non-critical and recoverable error.

My hope is to provide to the developer a better tool, to detect at compile time such problems.

If we wrote

----
// header file foo.hpp
void foo(const G& g) noexcept;

// source file foo.cpp

void foo(const G& g) noexcept(strong) {
    bar(g);
    g.baz();
}
----

and the copy-construtor of `g` could throw, or `bar` or `baz` or any other hidden function call, then the program would be ill-formed, and the compiler needs to diagnostic an error.

What are the advantages?
Introducing those kind of bugs wouldn't happen silently anymore.

f the code does not compile, we know that we need to inspect the `foo` function, because we probably cannot guarantee anymore if `foo` is `noexcept`.
Maybe it is not unreasonable to make a `bar() noexcept` function that mimics `bar noexcept(false)`. Maybe we do not need all the work done by `bar noexcept (false)`.

What if the function is not marked as `noexcept`, but it never throws an exception?
We have a few options:

    - Change the function signature and add the `noexcept` specifier, similar to what we would to with `const`;
    - Mark our function as `noexcept` or `noexcept(true)`, and not `noexcept(strong)`, and acknowledge with a comment why we are marking it as such (maybe the error will never happen at run time, but we can't prove it at compile time);
    - If it makes sense, we could split `foo` in order to use `noexcept(strong)` on a subset of operations.


Notice that similar to parameter names or the `const` specifier, when passing parameters by value, the `noexcept(strong)` specifier should be an implementation detail.

To the caller it makes no difference if the function is `noexcept` or `noexcept(strong)`.
In both cases the function is not going to throw an exception.

The important difference is for the implementer of the function.


This also means (I've done some digging into the standard) that:
The paragraph "5.3.7 noexcept operator" can remain unchanged, the `noexcept` operator still evaluates to `bool`.

The paragraph "15.4 Exception specifications" states that `noexcept` takes something that is convertible to `bool`.
Therefore `strong` should be convertible to `true`.
And this part

"An implementation shall not reject an expression merely because when executed it throws or might throw
an exception that the containing function does not allow"

is the core part I would like to update with `noexcept(strong)`.
Maybe something like

"An implementation shall not reject an expression merely because when executed it throws or might throw
an exception that the containing function does not allow, unless it is marked as noexcept(strong)"

could suffice.
An maybe extend the examples to something like:

----
extern void f() throw(X, Y);
void g() throw(X) {
    f();                    // OK
}

extern void f() throw(X, Y);
void g() noexcept {
    f();                    // OK
}

extern void f() throw(X, Y);
void g() noexcept(strong) {
    f();                    // ill-formed
}
----

Now, after digging into he standard and thinking about possible use-cases, I finally saw the biggest issue with such a proposal: generic code and therefore function templates.

Let us consider the `std::transform` algorithm.
It is not marked as `noexcept`, because in many cases, it will not be.
It depends on it's input parameters, consider

----
foo() noexcept(strong) {
    int arr[] = {1,2,3,4,5};
    std::transform(begin(arr), end(arr), begin(arr), [](int i)noexcept{return i+1;});
}
----

I would like this program to compile. `begin` and `end` are already `noexcept`, and I've marked the lambda as such.
I would expect that on all implementation, this algorithm will not allocate memory or do other operations that might cause an exception. It might make sense to expect it to be conditionally `noexcept`.


Of course something like

----
foo() noexcept(strong) {
    int arr[] = {1,2,3,4,5};
    for(auto a& : arr){
        a++;
    }
}
----

would compile just fine, but it would be a huge step back for reusability.
I think it should be possible to write `std::transform` in a way to be conditionally `noexcept`, just like `std::pair`.
If possible, such an implementation would have similar problems to those that `noexcept(strong)` would like to solve.
It would be nice having something like `noexcept(auto)`, like proposed in n4773 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4473), but as far as I know no one is working on it.
I also saw that Stroustrup (www.stroustrup.com/N3202-noexcept.pdf) wrote something about `noexcept`-deduction.

Unfortunately, I could not find any recent papers or proposals.
I think that `noexcept`-correctness and `noexcept`-deduction in templates are two important topics in order to write robust code.
It would ease a lot writing exception-safe code, which is a tremendously important property.

Nicol Bolas

unread,
May 11, 2018, 2:41:20 PM5/11/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
The basic problem I have with this is that it requires that everybody play on the same team. Either everyone examines every function for `noexcept`, or what you're doing doesn't work very well.

Right now, `noexcept` matters primarily for indicating a local construct of your program. A move constructor is `noexcept` because it doesn't do anything that might throw exceptions. If it happens to call some subsidiary functions to copy pointers or whatever, those functions don't need to be explicitly marked `noexcept`.

You put exception specifications on things where you have some reasonable expectation that someone is going to, at compile-time, do something different based on that. That's not everywhere; it's not a part of most functions.

You're trying to promote a world where every function needs to consider whether it ought to be `noexcept`. And that's just not a reasonable world. Especially if whether you're `noexcept` or not is conditioned based on the exception specification of user-provided code.

Is `std::sort` `noexcept`? Well, that would depend on whether the comparison function is `noexcept`. Do we really want every kind of algorithm to have to write some complex exception specification?

federico...@gmail.com

unread,
May 11, 2018, 5:07:29 PM5/11/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
Hi Nicol, thank you for your time and feedback


On Friday, May 11, 2018 at 8:41:20 PM UTC+2, Nicol Bolas wrote:
The basic problem I have with this is that it requires that everybody play on the same team. Either everyone examines every function for `noexcept`, or what you're doing doesn't work very well.

Yes, this is true.
But look at pair, optional, tuple, variant, array...
They are all conditionally `noexcept`.
So we already have the situation that for some constructs we need to pay attention if our code is (conditionally) `noexcept`.
And the compiler won't tell us if we did something wrong!

With `constexpr` and `const` it's different, since calling a non-constexpr function at compile time yields an error, and mutating a const variable is a compile error too.
You could argue with `constexpr` the same way about `noexcept` (or `const`). We all need to play on the same team.
And look at <algorithm>. For c++20 different algorithms like find (https://en.cppreference.com/w/cpp/algorithm/find) are marked as constexpr.

 
Right now, `noexcept` matters primarily for indicating a local construct of your program. A move constructor is `noexcept` because it doesn't do anything that might throw exceptions. If it happens to call some subsidiary functions to copy pointers or whatever, those functions don't need to be explicitly marked `noexcept`.

No, those function called in a move constructor do not need to be marked as such, but it would be great if they were, just to avoid hidden bugs. And remember that noexcept is already part of the function signature(!).

And like you said, it is a local construct.
Why should the whole program suffer a non-recoverable error that could get detected at compile time, if locally we mess something up?
It's not like corrupting memory, which invalidates the state of the program. (Notice: I'm not saying that terminating when throwing inside noexcept is bad!)
 
You put exception specifications on things where you have some reasonable expectation that someone is going to, at compile-time, do something different based on that. That's not everywhere; it's not a part of most functions.

Yes, and that's why I believe the problem you are describing are not so dramatic.
Most function will continue to be `noexcept(false)` as they are today, and as most functions are not constexpr.
For some functions, `noexcept` is really relevant, like destructor, moving operations and so on.
And those functions, normally, do not call a lot of code.
So we do not need to revisit immediately our code-basis and apply `noexcept` to every function that does not throw by accident. We do not need it.
Highly generic and reusable functions (like algorithms, pair, tuple, ...), would greatly benefit from something like `noexcept(auto)`, since by they nature they have a greater probability to get called in a context where being `noexcept` is relevant.
 
You're trying to promote a world where every function needs to consider whether it ought to be `noexcept`. And that's just not a reasonable world. Especially if whether you're `noexcept` or not is conditioned based on the exception specification of user-provided code.

We already live in a world where we need to think if a function might throw or not, and at least for three reasons:
 - write exception-safe code
 - consider execution paths/code flows when reading the program
 - documentation (we do not always have the source code available, so we need to document if functions are going to throw something, like all function of std are already documented)
I'm proposing to let the compiler use that information.
I'm also not proposing to deprecate `noexcept(true)` in favor of `noexcept(strong)`.
So we can still use `noexcept(false)` functions whenever we want.

Is `std::sort` `noexcept`? Well, that would depend on whether the comparison function is `noexcept`. Do we really want every kind of algorithm to have to write some complex exception specification?

It also depends on the iterators, move constructor, if the comparison functions takes by copy or reference, if there are hidden conversions... and maybe something else that I've forgot.
That's why it is important to let the compiler check if everything is `noexcept` or not (like described in n4773), if we need a `noexcept` or conditionally `noexcept` function.

I believe, it would make little sense to standardize something like `noexcept(strong)` without something like `noexcept(auto)`, because the two specification serve different purposes, but they would work together very well.
 

Nicol Bolas

unread,
May 11, 2018, 7:44:31 PM5/11/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
On Friday, May 11, 2018 at 5:07:29 PM UTC-4, federico...@gmail.com wrote:
I believe, it would make little sense to standardize something like `noexcept(strong)` without something like `noexcept(auto)`, because the two specification serve different purposes, but they would work together very well.

That's effectively agreeing with me that `noexcept(strong)` will result in more people writing `noexcept` specifications where they would not otherwise need to.

`noexcept` isn't supposed to be like `constexpr`. `constexpr` cannot call non-`constexpr` functions because only `constexpr` functions can execute at compile-time (in theory). The C++ constant expression model simply doesn't allow for anything else.

`noexcept` is not meant to work that way. A `noexcept` function calling a non-`noexcept` function is perfectly valid code. This is precisely why we don't need `noexcept(auto)`: because if I need to call `std::copy`, and I know that all of the operations it will use don't throw (for the provided types), I don't need `std::copy` to tell me it doesn't throw.

The lack of a `noexcept` specification does not mean "throws exceptions", and we should not assume that it does.

`noexcept(strong)` divides C++ functions into two groups: those that have noexcept specifications and those that don't. That's why you need `noexcept(auto)`; so that users can more easily sort themselves into a group.

The last thing the C++ language needs is to divide functions into yet another grouping.

Nicol Bolas

unread,
May 11, 2018, 7:54:22 PM5/11/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
On Friday, May 11, 2018 at 5:07:29 PM UTC-4, federico...@gmail.com wrote:
On Friday, May 11, 2018 at 8:41:20 PM UTC+2, Nicol Bolas wrote:
The basic problem I have with this is that it requires that everybody play on the same team. Either everyone examines every function for `noexcept`, or what you're doing doesn't work very well.

Yes, this is true.
But look at pair, optional, tuple, variant, array...
They are all conditionally `noexcept`.

Sorry; I missed this point in my previous reply.

Types are not "conditionally `noexcept`". Certain operations of those types are conditionally `noexcept`.

Those types are wrappers which attempt, where possible, to behave identically to the wrapped type. That's why the `noexcept` status of some of their operations are conditional; they're forwarding the `noexcept` status of the internal type(s).

Such forwarding is necessary only because those operations are ones which care very much about throwing exceptions: default initialization, copy/move operations, swapping, etc. There are very specific issues surrounding very specific operations. The optimizations you can do when you know a copy or move constructor won't throw are different from if you don't know that.

These issues are why `noexcept` was invented in the first place.

Algorithms, for example, make no effort to forward `noexcept`. Because they're not expected to do so. Even `std::copy` doesn't forward exception behavior; external code is not going to optimize based on whether `std::copy` throws or not.

This is very different from the use you are trying to make of `noexcept`. You're trying to turn syntax that was intended to solve a special case problem into something that is intended to solve a very different problem.

Vicente J. Botet Escriba

unread,
May 12, 2018, 3:49:00 AM5/12/18
to std-pr...@isocpp.org, federico...@gmail.com
Hi,

I believe the problem is orthogonal whether the function is conditional
noexcept is really no except as I would like to the following program to
be ill formed as well when g.baz() becomes no noexcept after some changes.


````
void foo(const G& g) noexcept(noexcept(bar(g))) {
    bar(g);
    g.baz();
}
`````


So maybe we need an additional annotation __STRICT__,

````
void foo(const G& g) __STRICT__ noexcept(noexcept(bar(g))) {
    bar(g);
    g.baz();
}
`````

or that noexpect accepts an additional label __STRICT__

````
void foo(const G& g) noexcept( __STRICT__, noexcept(bar(g))) {
    bar(g);
    g.baz();
}
`````


Have you considered the possibility to have an attribute and the
possibility that this could be a QOI and that the compiler could warn in
those situations?

````
void foo(const G& g) [[strict]] noexcept(noexcept(bar(g))) {
    bar(g);
    g.baz();
}
`````

This could be useful to experiment with the idea before making code that
is valid today ill formed.


Vicente

j c

unread,
May 12, 2018, 4:57:42 AM5/12/18
to std-pr...@isocpp.org, federico...@gmail.com


On Friday, May 11, 2018, Nicol Bolas <jmck...@gmail.com> wrote:
The basic problem I have with this is that it requires that everybody play on the same team. Either everyone examines every function for `noexcept`, or what you're doing doesn't work very well.

What team? Back when Symbian OS was a thing they used scan code for uncaught exceptions by examining the naming conventions of functions in a call chain. Clearly not 100% accurate, so if the compiler can point out a potential exception leak then it should. It would also help catch any accidental behavioural breaks. The Software Engineering team would appreciate something like this.


Right now, `noexcept` matters primarily for indicating a local construct of your program. A move constructor is `noexcept` because it doesn't do anything that might throw exceptions. If it happens to call some subsidiary functions to copy pointers or whatever, those functions don't need to be explicitly marked `noexcept`.

You put exception specifications on things where you have some reasonable expectation that someone is going to, at compile-time, do something different based on that. That's not everywhere; it's not a part of most functions.

You're trying to promote a world where every function needs to consider whether it ought to be `noexcept`. And that's just not a reasonable world. Especially if whether you're `noexcept` or not is conditioned based on the exception specification of user-provided code.

It's reasonable, and useful, in the Java world. And I'd imagine that it's required in the embedded/nuclear power plant world too, or maybe it's easier to reason about a program's behaviour by just turning exceptions off (Google were right all along)


Is `std::sort` `noexcept`? Well, that would depend on whether the comparison function is `noexcept`. Do we really want every kind of algorithm to have to write some complex exception specification?

 Yes, but complex in what way? It's a yes/no answer as to whether a function throws or not.
 

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/35f89aa3-02e6-4306-a120-18cd3ebc5ddf%40isocpp.org.

federico...@gmail.com

unread,
May 13, 2018, 3:56:07 AM5/13/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
@Vicente
Hi, thank you for your thoughts.


> I believe the problem is orthogonal whether the function is conditional
> noexcept is really no except as I would like to the following program to
> be ill formed as well when g.baz() becomes no noexcept after some changes.
>
> ...
>

Well, if you want to test a single expression, I see no advantages over something like

````
void foo(const G& g) noexcept(noexcept(bar(g))) {
    bar(g);
    static_assert(noexcept(std::declval<G>().baz(), "ERROR"));
    g.baz();
}
````

In my sample the test is more near to the expression we are actually testing.
It also means that if in the future we will remove the call to `baz`, it is easier not to miss the `static_assert` and remove it too.

If we have only some subroutines that are `noexcept`, we can always wrap the other in another function and mark it `noexcept`, for example:

````
// baz is not marked as noexcept, we cannot change it, but it should never throw (according to documentation)
void call_not_noexcept_func(const G& g) noexcept { // not noexcept strong(!)
    g.baz();
}

void foo(const G& g) noexcept(strong) { // thanks to call_not_noexcept_func we can use noexcept(strong) to test at compile time all other expressions
    bar(g);
    static_assert(noexcept(std::declval<G>().baz(), "ERROR");
    call_not_noexcept_func(g);
}
````

And in case of a terminate, we already know that we need to inspect only `call_not_noexcept_func`, since `foo` is checked at compile time.
We have narrowed the scope where we are not `noexcept`-correct, which is generally a good thing.

And we do not need to introduce a completely new syntax (`noexcept` taking multiple parameters).
I do not know if there are other changes we should apply to the standard, but with my proposal they would not be that big.
Whereas introducing a new syntax will surely require more wording.



> Have you considered the possibility to have an attribute and the
> possibility that this could be a QOI and that the compiler could warn in
> those situations?

Yes, I've considered the possibility and think that it would be suboptimal.
It would be like saying "we do not have const, but we could make an attribute ..."
I would really like `noexcept`-correctness to be like `const`-correctness.
If `noexcept(strong)` will never be a thing, of course I would try an attribute, but the other way around:
If the attribute is present, a compiler should not warn if there is a `throw` statement or `noexcept(false)` function called into a `noexcept(true)` function.
Because my hope is that in this case

````

void foo(const G& g) noexcept {
    // nested into the function, maybe after some condition
    throw g;
}
````

the compiler will warn us!


> This could be useful to experiment with the idea before making code that
> is valid today ill formed.

Which code would my proposal invalidate?
It is not a breaking change.


@Nicol:

> A `noexcept` function calling a non-`noexcept` function is perfectly valid code.
I'm not proposing to mark such code as invalid.
But even if it is valid, there are situations where is it questionable.


> and I know that all of the operations it will use don't throw (for the provided types)
You know the moment you wrote the code that it does not throw (modulo errors).
But the used class or container could change over time, and now some operations do throw, but your routine is still marked as noexcept.
Now you have a silent terminate inside a routine that no one noticed.

And testing if a class/routine really does not throw is normally difficult, look at the standard library.
The primary cause for exceptions are `bad_alloc`.
All other exceptions are because of programming error (like an out of bound access).
I assume that most programmers, try to write their code in such a way that those exceptions are never triggered.

So there is this routine which is `noexcept`, and by just reading the code we might assume that all operations are `noexcept`....
Can we test if our assumptions are true? Can we test it easily?
Because I've noticed that just reading the code is not always sufficient.

Another place where it is important not to fail is when you are already handling an exception.
Normally you do not want to override by accident the first one or crash while trying to report important information that would help you to analyze the error.

And there might be other places too, for example inside a callback function called by external libraries, since they might be have written in C.
In case of an exception they would leak resources or get in an invalid state.


> Types are not "conditionally `noexcept`". Certain operations of those types are conditionally `noexcept`.
Yes, sorry.


> Such forwarding is necessary only because those operations are ones which care very much about throwing exceptions: default initialization, copy/move operations, swapping, etc. There are very specific issues surrounding very specific operations. The optimizations you can do when you know a copy or move constructor won't throw are different from if you don't know that.

And we do an error while writing those wrapper, the compiler wont complain.

I'm not talking about possible optimizations, I'm talking about code-correctness.

The constructor of `unique-ptr` for example is `noexcept`.
Not because the underlying type might be `noexcept`, and not because someone said "just make it noexcept because I like it to behave similar to a pointer", but because we need that guarantee to write leak-safe code.

Is this code leak-free?

````
T* T = nullptr;
load_memory(&t); // C-function, does not throw (even if not marked with noexcept), on success T is not null, otherwise null
unique_ptr<T> handle{t};
````

Yes, it is (supposing that `load_memory` does not leak) and the reason is that the constructor of `unique_ptr` is `noexcept`.

And what about this code this (before C++14, now we should use make_unique)

````
unique_ptr<T> handle{new T{}};
````


If `unique_ptr<T>` would throw, then we would have a leak.


> These issues are why `noexcept` was invented in the first place.

As I just showed you, `noexcept` is also important in other context too, not only for moving and swapping.

It is used for marking code that will not fail (if certain invariants hold).
For example many container functions like `size()`, `empty()`, `clear()`, `begin()`, `end()` and others are `noexcept` too.
`std::string` `c_str()`, `data()` and other are `noexcept`.
And we are not constructing, swapping or moving.


> Algorithms, for example, make no effort to forward `noexcept`. Because they're not expected to do so. Even `std::copy` doesn't forward exception behavior; external code is not going to optimize based on whether `std::copy` throws or not.
Would you expect them to be const-correct?
In C there is no such expectation (see `strchr` for example), but in C++ I would expect every algorithm to be const-correct.
So why no noexcept-correct? Probably because noexcept came much later compared to const.

And again: its not about optimization, but about correcntess (same reason I want algorithm to be const-correct, not because the compiler might optimize something away).

Nicol Bolas

unread,
May 13, 2018, 9:36:27 AM5/13/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
On Sunday, May 13, 2018 at 3:56:07 AM UTC-4, federico...@gmail.com wrote:
@Nicol:
> Such forwarding is necessary only because those operations are ones which care very much about throwing exceptions: default initialization, copy/move operations, swapping, etc. There are very specific issues surrounding very specific operations. The optimizations you can do when you know a copy or move constructor won't throw are different from if you don't know that.

And we do an error while writing those wrapper, the compiler wont complain.

I'm not talking about possible optimizations, I'm talking about code-correctness.

`noexcept` was not created to promote code-correctness. That's why it's not like `const` or `constexpr`. We don't want it to be used to promote code correctness, because to do that requires massive proliferation of `noexcept` in peoples' code.

`throw` specifications were created to promote code-correctness. We ditched them because they were a terrible idea. We created `noexcept` because it was important for certain very specific use cases. Let's not turn `noexcept` into `throw` specifications 2.0.

The constructor of `unique-ptr` for example is `noexcept`.
Not because the underlying type might be `noexcept`, and not because someone said "just make it noexcept because I like it to behave similar to a pointer", but because we need that guarantee to write leak-safe code.

No, you don't. Your code will be leak-safe because the constructor doesn't throw, not because it is declared `noexcept`. The latter is simply one expression of the former; it is not the only one, and it should not be considered to be the only one.

Basically, I contest your entire premise that any function which is not declared `noexcept` must be expected to throw. That's simply not how C++ code is written.

Gašper Ažman

unread,
May 14, 2018, 3:39:43 PM5/14/18
to std-pr...@isocpp.org, federico...@gmail.com
I think a compelling reason for why noexcept functions don't have to be constructed solely of other noexcept functions (modulo try / catch blocks) is that a given function might have two contracts - a wide one which may throw, and a narrow one which definitely won't. If the caller can stay within the narrow contract, they can get noexcept guarantees, and sometimes that's only known at runtime.

Noexcept is a *really* strong guarantee.

G

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

federico...@gmail.com

unread,
May 17, 2018, 3:51:41 PM5/17/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com

The constructor of `unique-ptr` for example is `noexcept`.
Not because the underlying type might be `noexcept`, and not because someone said "just make it noexcept because I like it to behave similar to a pointer", but because we need that guarantee to write leak-safe code.

No, you don't. Your code will be leak-safe because the constructor doesn't throw, not because it is declared `noexcept`. The latter is simply one expression of the former; it is not the only one, and it should not be considered to be the only one.

Basically, I contest your entire premise that any function which is not declared `noexcept` must be expected to throw. That's simply not how C++ code is written.

Well, we clearly have completely different views for what noexcept can be used, and how we use it.
I researched the  topic a little bit, as far as I can see it is recommended practice to mark functions that we do not ever want to fail with `noexcept`, and the standard library seems to do so too (see my example with `unique_ptr`, or all the functions I've listed for containers, but no only those).

Do you mind to share some resource on the topic, since you claimed that my premise is wrong (every function not marked as noexcept should be considered as a function that could throw, with minor exceptions like C functions, functions marked with throw(), operations on fundamental types and so on).

And even if "noexcept` was not created to promote code-correctness", it does not mean that it cannot be used to promote it.
Templates where also not created to do compile-time computations...


I do not see how `noexcept(strong)` (without removing `noexcept(true)`) would make `noexcept` more similar to `throw()`, since `noexcept` is at compile time and throw specification where checked at runtime...

Nicol Bolas

unread,
May 17, 2018, 9:23:12 PM5/17/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
On Thursday, May 17, 2018 at 3:51:41 PM UTC-4, federico...@gmail.com wrote:

The constructor of `unique-ptr` for example is `noexcept`.
Not because the underlying type might be `noexcept`, and not because someone said "just make it noexcept because I like it to behave similar to a pointer", but because we need that guarantee to write leak-safe code.

No, you don't. Your code will be leak-safe because the constructor doesn't throw, not because it is declared `noexcept`. The latter is simply one expression of the former; it is not the only one, and it should not be considered to be the only one.

Basically, I contest your entire premise that any function which is not declared `noexcept` must be expected to throw. That's simply not how C++ code is written.

Well, we clearly have completely different views for what noexcept can be used, and how we use it.
I researched the  topic a little bit, as far as I can see it is recommended practice to mark functions that we do not ever want to fail with `noexcept`, and the standard library seems to do so too (see my example with `unique_ptr`, or all the functions I've listed for containers, but no only those).

Here are a few counter-examples from the standard library:

* The entire random number generation facility. There are a bunch of blanket statements like, "Except where specified otherwise, no function described in this section [rand.eng]/[rand. throws an exception." Yet not one of those function is `noexcept`. Nor are the distribution functions marked `noexcept`, even conditionally.

* The new elementary string conversion functions, `to_chars/from_chars`. Not only do they not throw, they have their own error mechanism, yet they lack `noexcept`.

* The entire standard algorithms library. They pass exceptions through them, but none of them throw anything themselves. Yet they are not conditionally `noexcept`.

And these are the ones I came up with because those chapters were the closest ones to where my spec PDF happened to open up.

My views on `noexcept` reflect both the intent of the feature's design and the reality of its use.

Do you mind to share some resource on the topic, since you claimed that my premise is wrong (every function not marked as noexcept should be considered as a function that could throw, with minor exceptions like C functions, functions marked with throw(), operations on fundamental types and so on).

Look around. How many C++ libraries do you know of that practice a strict policy of `noexcept` decorations on everything that genuinely doesn't throw exceptions? Boost? Poco? Qt? You can't even say this about the C++ standard library.

And even if "noexcept` was not created to promote code-correctness", it does not mean that it cannot be used to promote it.
Templates where also not created to do compile-time computations...

Yes, and we're trying our best to stop doing that. That's one of the reasons for `constexpr` stuff; so that we can stop using templates to do what ought to be done in something that looks like actual C++. It's the same reason why we want to have static reflection through a `constexpr` interface rather than a template-based one.

C++ has enough misappropriation of features for unintended purposes; let's not encourage it.

Tony V E

unread,
May 17, 2018, 9:29:21 PM5/17/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
The main reason for std functions that don't throw, but don't say noexcept, is that we allow implementations to throw on precondition violations.

Otherwise, most nonthrowing functions would be marked noexcept. Or conditionally noexcept.

With Contracts, our policies may change. (if contracts were checked outside the noexcept...)


Sent from my BlackBerry portable Babbage Device
From: Nicol Bolas
Sent: Thursday, May 17, 2018 9:23 PM
To: ISO C++ Standard - Future Proposals
Subject: Re: [std-proposals] noexcept-correctenss

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
May 18, 2018, 1:28:52 AM5/18/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
On Thursday, May 17, 2018 at 9:29:21 PM UTC-4, Tony V E wrote:
The main reason for std functions that don't throw, but don't say noexcept, is that we allow implementations to throw on precondition violations.

Otherwise, most nonthrowing functions would be marked noexcept. Or conditionally noexcept

So what about the ones that have no preconditions to violate? For example, `uniform_int_distribution::a` and similar functions.

This all seems less like deliberate design and more like nobody spent the time doing a full `noexcept` pass over the library. Which is exactly my point: if the standards committee hasn't done that for the feature they themselves created, why would you expect other C++ libraries to do the same? And without doing a full `noexcept` pass over a library, you can't gain much from `noexcept(strong)`.

federico...@gmail.com

unread,
May 18, 2018, 1:49:10 AM5/18/18
to ISO C++ Standard - Future Proposals, federico...@gmail.com
All functions you mentioned (except for the random number generation facility that I do not know well enough), can fail (at least conditionally), and this is the main reason for not marking them as noexcept.


> The main reason for std functions that don't throw, but don't say noexcept, is that we allow implementations to throw on precondition violations.
As Tony said, implementations are allowed to throw on preconditions violations (did not think of it. checked iterators is a good example, AFAIK iterators are not marked `noexcept`, but they are not required to throw either).


> Look around. How many C++ libraries do you know of that practice a strict policy of `noexcept` decorations on everything that genuinely doesn't throw exceptions?

I guess not many (even if I do not know the internals of most libraries) and in my opinion it's because there are not really many static tools for verifying for noexcept-correctness.
From a stability point of view, it's easier to document a function as non-throwing, and if it throws, it is still a recoverable error for the main application, so not a blocking issue.
If it throws and it is marked as `noexcept`, it will terminate, and that's not exactly a great feature for a library or a good sign of stability.

But most big libraries I know and have used are also pre c++11 or more c-style... still I would not claim that many tools that came with c++11 should be used only in certain contexts because they are not widely used.
Most younger libraries I know, use noexcept and not only for moving, and they use it for guaranteeing that something will not fail, instead of writing a separate documentation.


> With Contracts, our policies may change. (if contracts were checked outside the noexcept...)
I admit that I did not know that contracts could influence `noexcept`, so I'll give a look at that proposal too.


Just for the record, here are some references I found when searching on the topic:

https://stackoverflow.com/questions/10787766/when-should-i-really-use-noexcept
Recommendations are
1) "[...] use it when it's obvious that the function will never throw."
2) "[...] semantics first [...] As a programmer reading code, the presence of noexcept is akin to that of const: it helps me better grok what may or may not happen. Therefore, it is worthwhile spending some time thinking about whether or not you know if the function will throw. For reminder, any kind of dynamic memory allocation may throw. [...] now on to the possible optimizations. [...]"

The consensus seems to be that if a function cannot and should not fail, then it should be marked as noexcept.


https://akrzemi1.wordpress.com/2014/04/24/noexcept-what-for/
1) Makes the standard example where `noexcept` is used in order to guarantee that `push_back` has a strong exception guarantee (which has nothing to do with optimizations)
2) "This is because the information that noexcept really is intended to convey is that the function never fails; not that it never throws! [...] 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 whatever else, the reasoning about basic (no leak, invariant preserved), strong (commit or rollback) and never-fail guarantee should still hold."

3) The last point, "More eager termiation" is particularly interesting, because with what I'm proposing we would be able to reduce those runtime errors.

4) From a comment (I swear it's not mine) "IMO using noexcept is like using const [...] Just like we don’t say we should not use const because libraries may ‘mutable’ themselves out of the const contract, it does equally not seem like a good idea to promote such a thing for what I would like to call noexcept correctness."


The consensus, again, seems to be that if a function cannot and should not fail, then it should be marked as noexcept.


https://google.github.io/styleguide/cppguide.html

1) "You may use noexcept when it is useful for performance if it accurately reflects the intended semantics of your function, i.e. that if an exception is somehow thrown from within the function body then it represents a fatal error."

2) "instead of writing a complicated noexcept clause that depends on whether a hash function can throw, for example, simply document that your component doesn’t support hash functions throwing and make it unconditionally noexcept."


The guideline puts a lot of focus on performance, and that writing a correct noexcept specifier is hard (`noexcept(auto)` would therefore help), and thus use comments (hopefully some `static_assert` too).


https://scottmeyers.blogspot.de/2014/03/declare-functions-noexcept-whenever.html
1) Well the title says all...

https://arne-mertz.de/2016/01/modern-c-features-keyword-noexcept/
1) "`noexcept` is worth keeping in mind, since it can help to reason about your code"

https://stackoverflow.com/questions/14593333/why-noexcept-is-not-enforced-at-compile-time?rq=1
Those are some considerations why `noexcept(true)` should not  verify at compile time that it is calling only `noexcept` code, it is surely worth reading, but also shows that it would be nice having `noexcept(strong)` alongside `noexcept(true)`.

Of course those are only opinions, they can be wrong and whatever.
Just because the majority says a thing it does not mean that it's right.
It seems (at least not only to me) that the primary reason for `noexcept` to exist is code correctness (like my example with `unique_ptr` or the example with `std::vector` and `push_back`).
The fact that throwing inside `noexcept` (compared to `throw`) is a call to `terminate` without stack unwinding is an optimization opportunity, and it would be wrong to dismiss it.
Any of the given points does not still narrow `noexcept` for moving and swapping, on the contrary, it shows that `noecept` has a much bigger audience.

I would love to see some resource why we should not use `noexcept` except in move or swap and make no attempt to validate such assumptions at compile time.
The only reason I could imagine is compile time, but I do not think that this would be an issue.
Reply all
Reply to author
Forward
0 new messages