noexcept

Discussion in 'C++' started by Marc, Apr 27, 2011.

  1. Marc

    Marc Guest

    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.
    Marc, Apr 27, 2011
    #1
    1. Advertising

  2. Marc

    SG Guest

    On 27 Apr., 22:02, Marc wrote:
    >
    > 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
    SG, Apr 30, 2011
    #2
    1. Advertising

  3. Marc

    Marc Guest

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

    SG wrote:

    >> 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.


    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.

    >> 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.


    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...

    >> 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.


    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.

    >> (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.


    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.
    Marc, Apr 30, 2011
    #3
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Stefan Ram
    Replies:
    12
    Views:
    605
    Joshua Maurice
    Apr 26, 2011
  2. restor

    Using noexcept in C++0x

    restor, Jun 21, 2011, in forum: C++
    Replies:
    1
    Views:
    394
    Balog Pal
    Jun 22, 2011
Loading...

Share This Page