noexcept

M

Marc

Hello,

the definition of the noexcept operator seems to imply that any call
to a function not explicitly marked as noexcept makes it answer false.
In particular, that would mean that
is_nothrow_copy_constructible < some_POD_type > :: value == false. Or
if I missed a paragraph about that, at least:
void f(){}
assert(noexcept(f())==false);

Is that really the case? Any reason the compiler is forbidden from
proving that f() can't throw when it has the definition at hand?


Also, there are many is_nothrow_* helpers, but not
is_nothrow_swappable. Is it because
noexcept(swap(declval<T&>(),declval<T&>())) is sufficient (several of
the is_nothrow_* helpers seem to have subtleties for arrays of void
that prevent them from being equivalent to a simple noexcept
expression)? I have types that can be move-assigned or swapped without
throwing but for which any constructor may throw, and I fear they will
miss on many optimizations.
 
S

SG

the definition of the noexcept operator seems to imply that any call
to a function not explicitly marked as noexcept makes it answer false.

No, this is not 100% correct. The definition refers to "non-throwing
exception specifications". This includes noexcept, noexcept(true) as
well as throw().
In particular, that would mean that
is_nothrow_copy_constructible < some_POD_type > :: value == false.
Or if I missed a paragraph about that, at least:
void f(){}
assert(noexcept(f())==false);

I don't know about the POD copy constructor (maybe it's implicitly
defined as noexcept?) but the assertion of yours about f should hold
as far as I can tell. I believe, anything else would get messy in case
declarations and definitions of functions are separated into different
translation units.
Is that really the case? Any reason the compiler is forbidden from
proving that f() can't throw when it has the definition at hand?

Compiler complexity and inconsistent results. What if you declare such
a function in a header and use the same "noexcept expression" in
another translation unit without having the definition available? I'd
be very much against inconsistent results for the _same_ function
across different TUs.
Also, there are many is_nothrow_* helpers, but not
is_nothrow_swappable. Is it because
noexcept(swap(declval<T&>(),declval<T&>())) is sufficient

Is it sufficient? --- Oh yes, it seems it is, since std::swap uses a
conditional noexcept specification.
(several of
the is_nothrow_* helpers seem to have subtleties for arrays of void
that prevent them from being equivalent to a simple noexcept
expression)? I have types that can be move-assigned or swapped without
throwing but for which any constructor may throw, and I fear they will
miss on many optimizations.

Hmm... that sounds odd. Can you give an example for one of these
types? I have some trouble imagining why swapping should be any
"easier" w.r.t. exception safety than move construction.

Cheers!
SG
 
M

Marc

(Note: I found later several texts, including a paper by Stroustrup,
raising much the same points)

SG said:
I don't know about the POD copy constructor (maybe it's implicitly
defined as noexcept?) but the assertion of yours about f should hold
as far as I can tell. I believe, anything else would get messy in case
declarations and definitions of functions are separated into different
translation units.

noexcept would return true only when it knows for sure that there is
no exception and would almost always be allowed to safely answer
false.
Compiler complexity and inconsistent results. What if you declare such
a function in a header and use the same "noexcept expression" in
another translation unit without having the definition available? I'd
be very much against inconsistent results for the _same_ function
across different TUs.

Complexity is not a reason to forbid anything. I can see why
inconsistent results would be confusing, but they sound acceptable to
me, as the noexcept specification will simply allow some
optimizations, and with inlining the compiler may already generate
different code for the same function in different places. And the
alternative is worse:
void f(args) noexcept(body){body};
with a late specified return type, you may even have to write the body
3 times...
Is it sufficient? --- Oh yes, it seems it is, since std::swap uses a
conditional noexcept specification.

Yes, that and I could specify that my overload of swap for my type is
noexcept (although the compiler should really be able to guess).

Actually, the noexcept specification of std::swap seems to be missing
is_nothrow_destructible, unless we want to make swap a memory leak.
Hmm... that sounds odd. Can you give an example for one of these
types? I have some trouble imagining why swapping should be any
"easier" w.r.t. exception safety than move construction.

With move construction, you have to somehow construct an object to put
in place of the moved object. So you can see move construction as
either construct+swap or steal+construct.

The example at hand is basically a vector with the invariant that the
capacity is at least 1. Swapping is just moving a few pointers around,
perfectly safe. Any construction (even a move construction) requires
an allocation.

Yes, this design is very inconvenient with respect to the move
semantics introduced in C++0x (ie no-zombies), but it was done long
before.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top