Using noexcept

1434 views
Skip to first unread message

Andrzej Krzemieński

unread,
Jun 14, 2011, 7:05:45 PM6/14/11
to
Hi,
I tried to compile some information about the history of noexcept
proposals and guidelines on how it should be used safely and
efficiently. I used ISO C++ Committee papers and C++ discussion groups
as the sources of information to write this article:
http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/

I hope some may find it useful.
Regards,
&rzej


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Martin B.

unread,
Jun 15, 2011, 4:01:54 AM6/15/11
to
On 15.06.2011 01:05, Andrzej Krzemieński wrote:
> Hi,
> I tried to compile some information about the history of noexcept
> proposals and guidelines on how it should be used safely and
> efficiently. I used ISO C++ Committee papers and C++ discussion groups
> as the sources of information to write this article:
> http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/
>
> I hope some may find it useful.
> Regards,
> &rzej
>
>

Great article! Very useful.

Let me highlight a few sections though:

> Another significant change in the
> second proposal was that compiler was
> no longer statically checking if a
> noexcept functions really do not throw
> exceptions. While this decision is
> controversial, it was chosen because:
>
> * It had been already implemented on a
> production-class compiler
> (Microsoft’s non-standard-conforming
> implementation of throw()), and proved
> successful.
> * The noexcept block for locally disabling
> the static check was not
> good enough for all situations, for
> instance it was not suitable for
> disabling the check in constructors’
> initialization lists.

I'm not sure I quite follow this. Do you extract this information from
the papers? I did a noexcept search on papers a while back and couldn't
find any rationale ...

> If the noexcept feature appears to
> you incomplete, half-baked, or prepared in
> a rush, note that all C++ Committee
> members agree with you. (...)

Have you been on committee meetings to be able to say this?


cheers,
Martin

--
Stop Software Patents
http://petition.stopsoftwarepatents.eu/841006602158/
http://www.ffii.org/

Andrzej Krzemieński

unread,
Jun 15, 2011, 9:00:26 AM6/15/11
to

> I'm not sure I quite follow this. Do you extract this information from
> the papers? I did a noexcept search on papers a while back and couldn't
> find any rationale ...

Hi, regarding your three specific questions that follow, I base my
statements on either ISO C++ Committee papers or, in one case, Daniel
Kr�gler's post in this discussion group.

> > Another significant change in the
> > second proposal was that compiler was
> > no longer statically checking if a
> > noexcept functions really do not throw
> > exceptions. While this decision is
> > controversial, it was chosen because:
>
> > * It had been already implemented on a
> > production-class compiler

> > (Microsoft�s non-standard-conforming


> > implementation of throw()), and proved
> > successful.

This is based on N2983, section "Existing Practice". Although existing
practice in Microsoft compiler is undefined-behavior on no-throw
exception specification violation; but this is exactly what N2983 was
proposing.

> > * The noexcept block for locally disabling
> > the static check was not
> > good enough for all situations, for
> > instance it was not suitable for

> > disabling the check in constructors�
> > initialization lists.

This is based on Daniel Kr�gler's post in c.l.c.m:
https://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/9adab213129c178/c82a8b47e24a5acb?hl=en&lnk=gst#c82a8b47e24a5acb
Daniel Kr�gler attends ISO C++ Committee meetings.

> > If the noexcept feature appears to
> > you incomplete, half-baked, or prepared in
> > a rush, note that all C++ Committee
> > members agree with you. (...)
>
> Have you been on committee meetings to be able to say this?

No, I have not. My statement is based on paper N3081. It contains
minutes from WG21 meeting held in March 2010, Pittsburg, PA, USA. A
big section thereof is devoted to different noexcept variants. The
picture that emerges from the paper is that it was very controversial
how the application would behave if noexcept specification was
violated, but there was even stronger agreement that _any_ solution
was better than none.

Regards,
&rzej


--

Thomas Richter

unread,
Jun 15, 2011, 9:01:04 AM6/15/11
to

Am 15.06.2011 01:05, schrieb Andrzej Krzemieński:
> Hi,
> I tried to compile some information about the history of noexcept
> proposals and guidelines on how it should be used safely and
> efficiently. I used ISO C++ Committee papers and C++ discussion groups
> as the sources of information to write this article:
> http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/

Thanks for the article, but I still find that it leaves one point open:
If "noexcept" guarantees that std::terminate is called if an exception
is thrown regardless of "noexcept", then in how far does it differ from
"throw()"? And why does it offer optimization opportunities throw() does
not offer?

So long,
Thomas

Martin B.

unread,
Jun 15, 2011, 6:44:13 PM6/15/11
to

On 15.06.2011 15:01, Thomas Richter wrote:
>
> Am 15.06.2011 01:05, schrieb Andrzej Krzemieński:
>> Hi,
>> I tried to compile some information about the history of noexcept
>> proposals and guidelines on how it should be used safely and
>> efficiently. I used ISO C++ Committee papers and C++ discussion groups
>> as the sources of information to write this article:
>> http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/
>
> Thanks for the article, but I still find that it leaves one point open:
> If "noexcept" guarantees that std::terminate is called if an exception
> is thrown regardless of "noexcept", then in how far does it differ from
> "throw()"? And why does it offer optimization opportunities throw() does
> not offer?
>

throw() requires the compiler to unwind the stack. noexcept does not.

cheers,
Martin

Howard Hinnant

unread,
Jun 15, 2011, 6:44:57 PM6/15/11
to

On Jun 15, 9:01 am, Thomas Richter <t...@math.tu-berlin.de> wrote:
> Am 15.06.2011 01:05, schrieb Andrzej Krzemieński:
>
>> http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/
>
> Thanks for the article, but I still find that it leaves one point open:
> If "noexcept" guarantees that std::terminate is called if an exception
> is thrown regardless of "noexcept", then in how far does it differ from
> "throw()"? And why does it offer optimization opportunities throw() does
> not offer?

throw() can not be inspected/detected by the programmer (in C++03).
noexcept can. That is a huge difference. And this leads to huge
optimization opportunities by the programmer:

1. Do fast algorithm if noexcept(some expression or trait) is true.
2. Otherwise do slower algorithm.

std::move_if_noexcept is just one example of an optimization based on
conditional noexcept.

Howard

Andrzej Krzemieński

unread,
Jun 15, 2011, 6:45:08 PM6/15/11
to

> Thanks for the article, but I still find that it leaves one point open:
> If "noexcept" guarantees that std::terminate is called if an exception
> is thrown regardless of "noexcept", then in how far does it differ from
> "throw()"? And why does it offer optimization opportunities throw() does
> not offer?

This is a fair question and I cannot fully answer it because I just
don't know, and couldn't find a clear answer anywhere. I also found a
couple of Committee members saying something similar to "I am not sure
myself, but I was told by compiler writers it would be an
improvement". Here is one thing that is certain. With "throw()"
implemented in standard-compliant way (do you think your compiler
implements it?), when you throw an exception that will later, many
levels higher, violate the no-throw specification, the run-time has to
call all destructors of automatic objects; then when we reach the no-
throw barrier the run-time needs to call std::unexpected() that might
do something or not, and then check if std::unexpected threw something
like std::bad_exception.
With "noexcept(true)" implemented in standard-compliant way, when you
throw an exception, the run-time can go ahead and check, even before
it tries to call any destructors, if the exception would reach the
noexcept boundary or function main boundary, and just call
std::terminate without any stack unwinding.

But I do not find this sufficiently convincing. Herb Sutter was
addressing this in his recent interview in Channel 9.
http://channel9.msdn.com/Shows/Going+Deep/Herb-Sutter-C-Questions-and-Answers
He said something like, "now you can implement noexcept [...] with
basically a single bit on the stack frame if you didn't haven't that
already, that says whether an exception is active, and a single test:
if exception is active, then terminate." And previously compilers had
to add implicit catch(...) on every "throw()" specification, but I
still do not understand why.

I wish at this point some compiler writer would intervene and provide
us with some explanation.

One very essential difference though, is that you can use trait
std::is_nothrow_constructible to detect "noexcept(true)". You could
ask if std::is_nothrow_constructible couldn't just be made to detect
"throw()". I guess it could, but there is no way to use throw() to say
something similar to:

template< typename T > void fun( T& a, T& b )
noexcept( noexcept(a + b) && noexcept(a * b) );

Regards,
&rzej

Thomas Richter

unread,
Jun 16, 2011, 6:59:36 PM6/16/11
to

Am 16.06.2011 00:44, schrieb Martin B.:
>
> On 15.06.2011 15:01, Thomas Richter wrote:
>>
>> Am 15.06.2011 01:05, schrieb Andrzej Krzemieński:
>>> Hi,
>>> I tried to compile some information about the history of noexcept
>>> proposals and guidelines on how it should be used safely and
>>> efficiently. I used ISO C++ Committee papers and C++ discussion groups
>>> as the sources of information to write this article:
>>> http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/
>>
>> Thanks for the article, but I still find that it leaves one point open:
>> If "noexcept" guarantees that std::terminate is called if an exception
>> is thrown regardless of "noexcept", then in how far does it differ from
>> "throw()"? And why does it offer optimization opportunities throw() does
>> not offer?
>>
>
> throw() requires the compiler to unwind the stack. noexcept does not.

In how far does it improve the situation? Basically, the compiler would still have to generate code to test whether an exception is trying to leave the function, and then call std::terminate, whereas if I had *no* exception specification, no such code would have to be generated in first place *at all*. IOW, you get less optimal code by adding "noexcept" - probably not as bad as "throw()" which requires an implicit "try-catch" block, but still additional code.

So in how far does that make things simpler or faster? I can
a) not check statically whether a function may throw because it is not required by the new standard, and
b) it is neither faster and/or creates less code than no exception specification at all,

so in which sense is this an improvement?

I would have understood if throwing an exception within noexcept would probably be implementation defined or undefined - in which case compilers would not need to generate *any* extra code at all for noexcept, including cleanup code or unwind information. I would also have understood if noexcept would be statically checked since then I had means to validate at compile time whether I might possibly run into IDB/UB; but in how far is the current resolution of the committee an improvement? It looks like the worst of both worlds.

Greetings,
Thomas

Rani Sharoni

unread,
Jun 16, 2011, 7:00:25 PM6/16/11
to

On Jun 15, 2:05 am, Andrzej Krzemie�ski <akrze...@gmail.com> wrote:
> I tried to compile some information about the history of noexcept
> proposals and guidelines on how it should be used safely and
> efficiently. I used ISO C++ Committee papers and C++ discussion groups
> as the sources of information to write this article:http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/
>
> I hope some may find it useful.

Hi Andrzej,

As someone that was partially involved in the noexcept work I
appreciate your article and find it useful.
You can fine more information about the reasoning for the noexcept
feature here:
http://cpp-next.com/archive/2009/10/exceptionally-moving/

The debate following the 'exceptionally-moving' article was the early
base for the conditional throw/noexcept feature were we basically
tried to allow throwing move constructor to be safely used.
Move constructor is generally considered being no-throw like swap but
in fact it will throw if the default constructor throws since on move
construction the source parameter should at least stay in default
constructed state (zombie destructible only state is fragile to
withstand).

For example (breaking C++03):
vector< map<string> > v( /* fill with something */ );
v.resize();

In some implementation the default constructor of std::map throws
hence the move constructor also throws.
Disallowing throwing move therefore breaks above valid C++03 code.
Users might also have throwing move from similar reasons (e.g. member
map<>).

We obviously wanted to avoid such draconian no-throw restriction while
allowing aggressive vector-like move optimizations and detectible
noexcept(condition) helps to achieve that.

In your article you advice against restricting the implementation by
using noexcept to allow future changes that throws. This suggestion is
problematic since noexcept indeed restricts the implementation BUT it
relaxes the callers to also call the function in non-throwing contexts
such as destructors. Even without having explicit noexcept changing
function that never threw to throw might break callers so careful
early thinking is required.

Good criteria for marking function as no-throw is whether it might be
needed for successful destruction:
1) Destructors - called by other destructors
2) cleanup functions like delete - throwing implies leak
3) remove functions like pop and map::erase
4) lookup functions - map::erase implicitly requires lookup
5) synchronization primitives - e.g.
'lock' to remove from private heap
'wait' for outstanding async callback
'signal' worker thread to exit
6) simple read-only 'get' operations like map::size

The above might be theoretically restricting/hard for the
implementation but simplify the life of unbounded number of callers.

Note that some non-throwing operation might return some status but
they must not fail to end the lifetime of the object. for example,
closing a remote file might fail to communicate remotely but the local
resource should be freed.

Thanks,
Rani

Rani Sharoni

unread,
Jun 17, 2011, 3:48:14 AM6/17/11
to
> the compiler would still have to generate code to test
> whether an exception is trying to leave the function,
> and then call std::terminate,

True. the problematic case is when the throw can be potentially masked
by outer catch.
For example:
try {
function_with_noexcept();
might_throw();
extern_c_function();
}
catch(...) {}

extern-c function (e.g. OS call) might not be aware about the noexcept
state-flag hence it's the caller's responsibility to toggle the flag
before/after calling into every throw/nothrow (might skip some).

I personally wanted UB to push such bug handling under QOI but it
seems that the committee though that UB for such unhandled exceptions
is somewhat bad PR for a language. OTOH, mandating mitigation for such
specific bug seems inconsistent with other cases that traditionally
are left for UB (e.g. access bogus memory).
I also had no problem with breaking the legacy throw() requirement on
violation since the popular MSVC compiler is doing that since always
(hence favoring optimizations).

> whereas if I had *no* exception specification,
> no such code would have to be generated in first place *at all*.
> IOW, you get less optimal code by adding "noexcept" -

This is not true. The compiler can avoid generating unwind code for
the throwing case which is the main no-throw optimization (caller and
callee). for example:
unique_ptr<A> p = new A;
nothrow_call(p);
// implicitly generate delete p, no need for special unwind on-throw
code
return;

> so in which sense is this an improvement?

The nexcept(condition) is important for no-throw based user
optimization (e.g. move if no-throw).

> I would also have understood if noexcept would be statically checked
> since then I had means to validate at compile time

Static validation is problematic in general due to conditional no-
throw functions (runtime).
For example:
vector<int> v(10); // reserve space for 10 elements
v.push_back(1); // no-throw since no allocation needed
map<int, int> m;
m[0] = 0;
m[0] = 1; // no-throw on replace

It seems that for static checking, noexcept needs to have another
flavor - "unconditionally might throw".
This is probably much more common than conditional no-throw, For
example:
void* operator new(size_t) noexcept(--);

void foo() noexcept
{
new A; // can be statically rejected
}

Note that the MSVC compiler issues a warning when explicit 'throw' is
used unguarded inside a throw() function.
The above non-standard idea extends such for other functions beside of
the 'throw' operator.
One concern about having such is with breaking the compilation of lots
of existing code that doesn't care about conditions such as out of
memory.

I think that static checking is nice to have but not super crucial
since anyway proper testing is needed given the many other bugs that
can be detected only at runtime or using custom static analysis tools
(e.g. all the traditional UB bugs).

Rani

Thomas Richter

unread,
Jun 17, 2011, 8:43:36 AM6/17/11
to

Am 17.06.2011 09:48, schrieb Rani Sharoni:
>> the compiler would still have to generate code to test
>> whether an exception is trying to leave the function,
>> and then call std::terminate,
>
> True. the problematic case is when the throw can be potentially masked
> by outer catch.
> For example:
> try {
> function_with_noexcept();
> might_throw();
> extern_c_function();
> }
> catch(...) {}
>
> extern-c function (e.g. OS call) might not be aware about the noexcept
> state-flag hence it's the caller's responsibility to toggle the flag
> before/after calling into every throw/nothrow (might skip some).

I afraid I couldn't quite follow you, could you please elaborate?

> I personally wanted UB to push such bug handling under QOI but it
> seems that the committee though that UB for such unhandled exceptions
> is somewhat bad PR for a language.

This sounds like a strange argument to me. Actually, there is lots of UB
already in C++ as it is right now, and it has ever been this way - if
the user does something stupid, well,...

> OTOH, mandating mitigation for such
> specific bug seems inconsistent with other cases that traditionally
> are left for UB (e.g. access bogus memory).

Indeed.

> I also had no problem with breaking the legacy throw() requirement on
> violation since the popular MSVC compiler is doing that since always
> (hence favoring optimizations).

Indeed.

>> whereas if I had *no* exception specification,
>> no such code would have to be generated in first place *at all*.
>> IOW, you get less optimal code by adding "noexcept" -
>
> This is not true. The compiler can avoid generating unwind code for
> the throwing case which is the main no-throw optimization (caller and
> callee). for example:
> unique_ptr<A> p = new A;
> nothrow_call(p);
> // implicitly generate delete p, no need for special unwind on-throw
> code
> return;

Ok, I see what you mean, but I don't quite agree with the argument.
Allow me to elaborate: Without a throw specification, the compiler has
to generate the unwind code, but this is usually "for free" since it is
not the common call path. Gcc generates rather a table in the binary
which code to call if stack unwinding is required. If I add a throw
specification to a calling function, the compiler has to generate both,
the stack unwinding code, and extra code to check whether the exception
thrown was actually specified, and if not, run into std::unexpected (or
something like this, I forgot the nasty details). Whereas now, with
noexcept, I get only half of the optimization. The compiler has *still*
to generate code to check whether an exception wants to leave the
function. It may only omit the unwind code. But that seems to be the
easy part for me (though maybe I'm wrong) the hard part is to check
whether there is actually any exception on the way.

Whereas if it would have been UB to throw an exception in noexcept, the
compiler would have to do nothing *at all* to handle such a case, and
thus there would be no code at all - hence no unwind code, and no
exception checking code.

>> so in which sense is this an improvement?
> The nexcept(condition) is important for no-throw based user
> optimization (e.g. move if no-throw).

That was the initial motivation as I understand. Well, maybe. Time will
show.

>> I would also have understood if noexcept would be statically checked
>> since then I had means to validate at compile time
>
> Static validation is problematic in general due to conditional no-
> throw functions (runtime).
> For example:
> vector<int> v(10); // reserve space for 10 elements
> v.push_back(1); // no-throw since no allocation needed
> map<int, int> m;
> m[0] = 0;
> m[0] = 1; // no-throw on replace
>
> It seems that for static checking, noexcept needs to have another
> flavor - "unconditionally might throw".

I don't think the compiler could be expected to prove in such cases that
the calls cannot throw. Actually, if I would have the *requirement* to
write a function that cannot throw, for whatever reason, I wouldn't use
the corresponding standard library functions, but rather build something
on my own. That is, you're on yourself if you need additional
requirements like static memory or static code. std::vector::push_back()
cannot be marked as noexcept since it may call new, and hence it isn't
noexcept. If you use it in a noexcept function, well, you have to pay
for the price and add a try-catch block around it. This block wouldn't
do anything except adding additional code - hopefully:

try {
v.push_back(1);
} catch(..) {
// Ooops, how did that happen now?
std::terminate();
}

IOW, it would require from the user to *manually* insert code the
compiler would have to insert otherwise to indicate that "yes, I know
this isn't optimal, I don't get the optimizations I might, but at least
its safe enough".

If you cannot live with that, well, use arrays, they are noexcept for sure.

> This is probably much more common than conditional no-throw, For
> example:
> void* operator new(size_t) noexcept(--);
>
> void foo() noexcept
> {
> new A; // can be statically rejected
> }

I don't understand? void *operator new(size_t) is definitely not
"noexcept" if the allocation fails. Unless you implement your own
version, which terminates the program manually if it runs out of memory,
and thus can be marked as "noexcept". In which case of course the above
code should compile. Of course, it is then up to the programmer
implementing oerator new() to ensure that the noexcept guarantee isn't
violated, but this is again beyond what the compiler can do.

> Note that the MSVC compiler issues a warning when explicit 'throw' is
> used unguarded inside a throw() function.

Well, I'm not using MSVC but rather gcc, but whatever; while a nice
"service" of the compiler, I would rather prefer to have this an error.
If you violate the contract, the compiler should error, not warn.

The following is exactly the same problem, and AFAIK an error in C++.

int foo(int args)
{
return; // oops!
}

Why isn't that a warning either? Because it breaks the contract. In the
same vain, throwing an exception in noexcept breaks the contract, and
hence should be an error. Hence, everything that *potentially* may throw
cannot be used in noexcept - unless the programmer knows that he has to
a pay a price for this, and add a try-catch around the code. Even *if*
he knows that the catch() will not be invoked due to reasons beyond what
the compiler may test for. It is just a safety feature in case the code
is modified later on though forgetting about the precise conditions
under which it was designed (such situations appear).

> The above non-standard idea extends such for other functions beside of
> the 'throw' operator.
> One concern about having such is with breaking the compilation of lots
> of existing code that doesn't care about conditions such as out of
> memory.

I don't quite understand? Current code doesn't use noexcept, thus there
shouldn't be a problem?

> I think that static checking is nice to have but not super crucial
> since anyway proper testing is needed given the many other bugs that
> can be detected only at runtime or using custom static analysis tools
> (e.g. all the traditional UB bugs).

Again, this sounds like a strange argument to me. I would prefer to
fixup the language - "just because we've been sloppy in the past, let's
continue to be sloppy". (-; No, I don't think so.

So long,

Thomas

Andrzej Krzemieński

unread,
Jun 17, 2011, 8:44:03 AM6/17/11
to

Hi,
I think I agree with Thomas's observation and I think his question has
not been addressed yet.
Everyone will agree that noexcept(true) specification may make a non-
throwing code faster when std::move_if_noexcept (or similar tool) is
used, because copying will be replaced with a way faster moving, and
in containers that store millions of elements this will be huge.
Second, everyone will agree that noexcept(true) offers performance
benefit over throw() once the function threw an exception that will
violate the no-throw boundary becauuse we need not call
std::unexpected or destructors.
But the most interesting question remains: does noexcept(true) offer
any performance benefit over throw() in situations where nothing is
thrown and program runs normally?
On the other hand, as Thomas said, a no-fail map::erase() executes
faster, when not annotated with either noexcept(true) or throw(), than
map::erase() annotated with noexcept(true) because in the latter case
the compiler must perform a check if std::terminate is required. Am I
right?

> > whereas if I had *no* exception specification,
> > no such code would have to be generated in first place *at all*.
> > IOW, you get less optimal code by adding "noexcept" -
>
> This is not true. The compiler can avoid generating unwind code for
> the throwing case which is the main no-throw optimization (caller and
> callee). for example:
> unique_ptr<A> p = new A;
> nothrow_call(p);
> // implicitly generate delete p, no need for special unwind on-throw

> // code
> return;

True, but wouldn't the same optimization work if function
nothrow_call() was annotated with throw() rather than noexcept(true)?

Regards,
&rzej

Andrzej Krzemieński

unread,
Jun 17, 2011, 8:44:20 AM6/17/11
to

> In your article you advice against restricting the implementation by
> using noexcept to allow future changes that throws. This suggestion is
> problematic since noexcept indeed restricts the implementation BUT it
> relaxes the callers to also call the function in non-throwing contexts
> such as destructors. Even without having explicit noexcept changing
> function that never threw to throw might break callers so careful
> early thinking is required.
>
> Good criteria for marking function as no-throw is whether it might be
> needed for successful destruction:
> 1) Destructors - called by other destructors
> 2) cleanup functions like delete - throwing implies leak
> 3) remove functions like pop and map::erase
> 4) lookup functions - map::erase implicitly requires lookup
> 5) synchronization primitives - e.g.
> 'lock' to remove from private heap
> 'wait' for outstanding async callback
> 'signal' worker thread to exit
> 6) simple read-only 'get' operations like map::size
>
> The above might be theoretically restricting/hard for the
> implementation but simplify the life of unbounded number of callers.
>
> Note that some non-throwing operation might return some status but
> they must not fail to end the lifetime of the object. for example,
> closing a remote file might fail to communicate remotely but the local
> resource should be freed.

Hi, thank you for your input. I do not mean to put recommendations
that are not in accord with the designers of the feature. But before I
change my post, could I run the following thought throug you?

I apparrently did not make it explicit enough, but I find two reasons
why functions, even today, are non-throwing (If you statically checked
them):

(1). Because they just happen not to throw. For example a function
sorting an array of integers:

void sort_ints( int(&array)[100] );

It does not throw because we use only built-in operations on
integers. But no-one really benefits form the fact that the function
doesn't throw. Every user will still assume it throws and use it as
though it could.

(2). Because they are meant to never throw. You have already provided
examples.

There will obviously be a "middle-ground" functions that cannot be
clearly cathegorized. Personally, I treat them as (1). That is, if
your function is not meant to provide a no-throw guarantee (as part of
the contract) do not restrict yourself too hard.

Regards,
&rzej

Rani Sharoni

unread,
Jun 18, 2011, 9:38:33 AM6/18/11
to

> But the most interesting question remains: does noexcept(true) offer
> any performance benefit over throw() in situations where nothing is
> thrown and program runs normally?

The current noexcept is probably similar to throw() since both magnate
violation handling.
I'm not sure about the exact details since I'm using the non
conforming MSVC compiler in which throw() is exactly what I want - UB
manifested for performance only biased implementation.
OTOH, I heard that MSVC also wants to confirm with noexcept violation
handling with claim that the "safety" overhead is quite low (but then
we have some many low-overhead safety feature that adds up) while
keeping the good old no-throw optimizations (no unwind code generated
for caller and callee).

I will obviously take any zero overhead bug mitigation (e.g. null
derefernce) but prefer it will be matter of QOI just in case the
overhead will turn out to be non-zero ;-)

Rani

Martin B.

unread,
Jun 18, 2011, 9:34:30 AM6/18/11
to

On 17.06.2011 00:59, Thomas Richter wrote:
>
> Am 16.06.2011 00:44, schrieb Martin B.:
>>
>> On 15.06.2011 15:01, Thomas Richter wrote:
>>>
>>> Am 15.06.2011 01:05, schrieb Andrzej Krzemieński:
>>>> Hi,
>>>> I tried to compile some information about the history of noexcept
>>>> proposals and guidelines ...

>>>
>>> Thanks for the article, but I still find that it leaves one point open:
>>> If "noexcept" guarantees that std::terminate is called if an exception
>>> is thrown regardless of "noexcept", then in how far does it differ from
>>> "throw()"? And why does it offer optimization opportunities throw() does
>>> not offer?
>>>
>>
>> throw() requires the compiler to unwind the stack. noexcept does not.
>
> In how far does it improve the situation? Basically, the compiler would
> still have to generate code to test whether an exception is trying to
> leave the function, and then call std::terminate, whereas if I had *no*
> exception specification, no such code would have to be generated in
> first place *at all*. IOW, you get less optimal code by adding
> "noexcept" - probably not as bad as "throw()" which requires an implicit
> "try-catch" block, but still additional code.
>

I'm absolutely no compiler expert, but as far as I can see, we have this:

(a) noexcept(false) == no specification
// Compiler needs to add an
// unwinding code path in the
// case of an exception, which essentially
// has to make sure x d'tor is called.
void f() noexcept(false) {
Object x;
potentially_throwing_fn(x);
}

(b) noexcept(true)
// Compiler needs to add a handling
// code path in the case of an
// exception, which has to make
// sure terminate() is called
void f() noexcept(true) {
Object x;
potentially_throwing_fn(x);
}

I'm not sure how noexcept(true) is a pessimization here?

cheers,
Martin

Rani Sharoni

unread,
Jun 18, 2011, 9:31:40 AM6/18/11
to

>> Good criteria for marking function as no-throw is whether it might be
>> needed for successful destruction:
> Hi, thank you for your input. I do not mean to put recommendations
> that are not in accord with the designers of the feature. But before I
> change my post, could I run the following thought throug you?
>
> (1). Because they just happen not to throw. For example a function
> sorting an array of integers:
>
> void sort_ints( int(&array)[100] );
>
> It does not throw because we use only built-in operations on
> integers. But no-one really benefits form the fact that the function
> doesn't throw. Every user will still assume it throws and use it as
> though it could.

I agree that some high level operations might be implemented as no-
throw yet they are not intended for no-throw contexts hence noexept
would be over specifying.

OTOH, consider low level operations like the standard algorithms. You
want to make such as useable as possible hence even as building blocks
of no-fail operations. Such algorithm indeed don't throw by themselves
yet they might throw due to user defined operations. for example,
std::copy will not throw for in memory collections of non-throwing
types like int. Another example is operations on iterators of standard
collection (e.g. being(), ++i, etc).

Note that noexcept(condition) and noexcept(expression) allow to
annotate such algorithms with the accurate noexcept(true/false) yet
this might be hard to specify in general hence implicit "conditional
no-throw" is useful for such (balance off verbose static checks to
favor clean code).

> (2). Because they are meant to never throw. You have already provided
> examples.
>
> There will obviously be a "middle-ground" functions that cannot be
> clearly cathegorized. Personally, I treat them as (1). That is, if
> your function is not meant to provide a no-throw guarantee (as part of
> the contract) do not restrict yourself too hard.

I agree that over-specification should be avoided when there is no
much return for restricting the implementation.
OTOH, some no-fail (throw or error-code) operations are not trivial to
implement though important for usability.

One surprising example is wcsicmp - C function for case insensitive
wide-string compare.
wcsicmp seems like no fail and indeed its C interface doesn't allow
errors.
BUT wcsicmp might depend on thread specific locale and such might be
allocated (in the per-thread-data) on demand.
Some existing CRT implementation will simply terminate the process in
case that such on-demand allocation fails.
There are some option to overcome this but they are not trivial given
specific threading environment.

Another example is throw itself. Obviously throwing an exception
should not fail but there is some heavy duty machinery associated with
throw and it's obviously per thread...

Note that languages like Java "does not" have such issues since the
runtime environment itself can't withstand out-of-memory failures ;-)
C/C++ is actually unique in allowing such robustness.

Rani

Rani Sharoni

unread,
Jun 18, 2011, 9:36:37 AM6/18/11
to

>> I personally wanted UB to push such bug handling under QOI but it
>> seems that the committee though that UB for such unhandled exceptions
>> is somewhat bad PR for a language.
>
> This sounds like a strange argument to me. Actually, there is lots of UB
> already in C++ as it is right now, and it has ever been this way - if
> the user does something stupid, well,...

Bug mitigation actually became popular in some environments due to
security concerns related to poorly written code and made such
"trendy" despite of the perf overhead.
For example: MSVC compiler supports stack corruption mitigation (AKA /
GS option), Win32 heap also has such, some MSVC CRT APIs has "secure"
version of memcpy and STL vendors provides "secure" mode (e.g. check
for bad iterators).

Note that noexcept violation is always a bug and on such the program
is anyway under the UB umbrella...

OTOH, being a QOI, the above bug handling mitigation can be enabled/
disabled by the user.
For example, we must use /GS but not secure STL. I don't expect safe
heaps tight embedded systems.

I'm not sure what makes unhandled exceptions more appealing for
standard mitigation especially when even the conservative win32 envs
are using compiler that never cared about such...

Note that unhandled-exceptions are among the most common bugs in
"throwing C++" and given aggressive "no unwind" optimization and
masking catches can lead to bugs like deadlocks (e.g. lock is not
released yet the unhandled exception was masked).

>>> IOW, you get less optimal code by adding "noexcept" -
>
>> This is not true. The compiler can avoid generating unwind code for
>> the throwing case which is the main no-throw optimization (caller and
>> callee).

> Ok, I see what you mean, but I don't quite agree with the argument.


> Allow me to elaborate: Without a throw specification, the compiler has
> to generate the unwind code, but this is usually "for free" since it is
> not the common call path. Gcc generates rather a table in the binary
> which code to call if stack unwinding is required.

Even with the unwind table approach (also done by x64-VC/win64) there
is still something that gets generated.
Now think generating such code for every operator-delete call site.
And then for destructors.
Such is expensive and for practically nothing.
I hope that compilers will be smart enough to avoid the noexcept EH
code for calls like delete.
Such also shows that UB is better since it allows more sophisticated
noexcept violation handling (e.g. avoid some for speedup).

> Whereas if it would have been UB to throw an exception in noexcept, the
> compiler would have to do nothing *at all* to handle such a case, and
> thus there would be no code at all - hence no unwind code, and no
> exception checking code.

Indeed. The compiler has more freedom to balance between safety and
performance with UB.
OTOH, compiler vendors are promising very low overhead safety...

>>> so in which sense is this an improvement?
>> The nexcept(condition) is important for no-throw based user
>> optimization (e.g. move if no-throw).
>
> That was the initial motivation as I understand. Well, maybe. Time will
> show.

The aggressive vector like move optimization potential is there but I
agree with you about whether it will actually matter.

>>> I would also have understood if noexcept would be statically checked
>>> since then I had means to validate at compile time
>
>> Static validation is problematic in general due to conditional no-
>> throw functions (runtime).
>> For example:
>> vector<int> v(10); // reserve space for 10 elements
>> v.push_back(1); // no-throw since no allocation needed
>> map<int, int> m;
>> m[0] = 0;
>> m[0] = 1; // no-throw on replace
>
>> It seems that for static checking, noexcept needs to have another
>> flavor - "unconditionally might throw".
>
> I don't think the compiler could be expected to prove in such cases that
> the calls cannot throw. Actually, if I would have the *requirement* to
> write a function that cannot throw, for whatever reason, I wouldn't use
> the corresponding standard library functions, but rather build something
> on my own.

The callers actually mainly wants to preallocate in order to avoid
perf impact in critical paths (e.g. insert under locks). why not use
vector::reserve that was invented to allow such and now has all the
move magic?
You might also fine may if(map[key]) checks in non-throwing contexts
(such can in general throw since it inserts on not found).

>> This is probably much more common than conditional no-throw, For
>> example:
>> void* operator new(size_t) noexcept(--);
>
>> void foo() noexcept
>> {
>> new A; // can be statically rejected
>> }
>
> I don't understand? void *operator new(size_t) is definitely not
> "noexcept" if the allocation fails.

Sorry for not being clear. I was using the imaginary noexcept(--)
syntax to mark function as "unconditionally might throw" since
noexcept(false) actually says that the function is allowed to be no-
throw hence disallowing static checking.

> Why isn't that a warning either? Because it breaks the contract. In the
> same vain, throwing an exception in noexcept breaks the contract, and
> hence should be an error. Hence, everything that *potentially* may throw
> cannot be used in noexcept -

This might be worthy to consider for new language but with C++ you
have much legacy code to consider and such makes your static check
impractical. OTOH, my suggestion for having another explicit notion
that says "not allowed from noexcept" seems less breaking (yet too
late for C++1x).

Note that I only talk about static checks for "unconditionally might
throw anything" and not the Java-like verbose/fragile exception
specifications that are statically checked.

> I don't quite understand? Current code doesn't use noexcept, thus there
> shouldn't be a problem?

As I said, I meant that functions like throwing operator-new should be
annotated to say "disallowed from noexcept" as it's done by some
compilers for explicit "throw" call from throw().

>> I think that static checking is nice to have but not super crucial
>> since anyway proper testing is needed given the many other bugs that
>> can be detected only at runtime or using custom static analysis tools
>> (e.g. all the traditional UB bugs).
>
> Again, this sounds like a strange argument to me. I would prefer to
> fixup the language - "just because we've been sloppy in the past, let's
> continue to be sloppy". (-; No, I don't think so.

I think that language designers tries to be careful with having
potentially polluting notations in the language despite of the good
intentions. Exception specifications is a prime example for such.
OTOH, I wonder what Prof. Stroustrup will think about having explicit
notion to disallow throwing calls from no-except (e.g. throw(true) for
throwing operator-new) ;-).

Thanks,
Rani

Reply all
Reply to author
Forward
0 new messages