22

Studying about "noexcept specifier(and operator)", I wrote a simple code. And I am surprised that this piece of code:

void asdf() noexcept {}
int main() 
{
    auto f = asdf;
    std::cout << std::boolalpha << noexcept(f()) << std::endl;
}

prints false, even function "asdf" is noexcept-specified.

So while searching why this mysterious phenomenon is happening, I found C++17's "exception specifier type system"- P0012R1.

According to this (accepted) proposal, since C++17; as noexcept is part of function type, will the code above print true?

And one more, in this question's one line:

std::function<void() noexcept> f

The noexcept specifying seems ignored in C++14 or 11. Will this code work as intended in C++17?

Community
  • 1
  • 1
suhdonghwi
  • 955
  • 1
  • 7
  • 20
  • g++ return `true` for `noexcept(f())` without `-std=c++1z` (but clang does return `false`). – Holt Aug 04 '16 at 07:23
  • 1
    For extra fun, change `auto f = asdf;` to `void(*f)() noexcept = asdf;`. Now GCC prints `false` while clang prints `true`. –  Aug 04 '16 at 07:28
  • @Holt Thanks for information. I am using MSVC(returns `false`). What would be different of them? Is that a bug of g++ or C++14 or earlier standard specified nothing about `noexcept` type system? – suhdonghwi Aug 04 '16 at 07:31
  • @hvd For that code, MSVC prints `true`. I'm so confused now :p – suhdonghwi Aug 04 '16 at 07:32
  • @hvd With the function pointer version, if you remove the `noexcept` on `asdf`, clang rejects the affectation but gcc does not complain... I am guessing gcc ignore `noexcept` on function pointer declaration. – Holt Aug 04 '16 at 07:41

1 Answers1

14

According to this (accepted) proposal, since C++17; as noexcept is part of function type, will the code above print true?

Yes.

The type of f will be deduced to void(*)() noexcept since the function-to-pointer conversion applied to asdf will preserve the noexcept property. A call to a noexcept function pointer certainly cannot throw an exception, unless one of its subexpressions does.

For the exact wording, see [expr.unary.noexcept]/3 and [expect.spec]/13. Note that the new wording in the latter paragraph from the C++17 draft comes from P0012R1, which is linked in the OP.

The result of the noexcept operator is true if the set of potential exceptions of the expression ([except.spec]) is empty, and false otherwise.

...

  • If e is a function call ([expr.call]):
    • If its postfix-expression is a (possibly parenthesized) id-expression ([expr.prim.id]), class member access ([expr.ref]), or pointer-to-member operation ([expr.mptr.oper]) whose cast-expression is an id-expression, S is the set of types in the exception specification of the entity selected by the contained id-expression (after overload resolution, if applicable). ...

So the set of potential exceptions of f() is the same as the set of types in the exception specification of f, which is empty since f is declared noexcept.

Let's move on to the second question:

The noexcept specifying seems ignored in C++14 or 11. Will this code work as intended in C++17?

Your question seems to be: will std::function<void() noexcept> refuse to hold a function that can throw exceptions?

I would say that it's unclear. In the present wording of the standard, std::function<void() noexcept> is not actually defined, just as std::function<double(float) const> is not defined. This was of course not an issue in C++14, since noexcept was not considered part of a function's type.

Will std::function<void() noexcept> simply break in C++17? That's uncertain to me. Let's take a look at the current wording to guess what the behaviour "should" be.

The standard requires the argument to the constructor of std::function<R(ArgTypes..)> to be "Lvalue-Callable" for argument types ArgTypes... and return type R, which means:

A callable type ([func.def]) F is Lvalue-Callable for argument types ArgTypes and return type R if the expression INVOKE(declval<F&>(), declval<ArgTypes>()..., R), considered as an unevaluated operand (Clause [expr]), is well formed ([func.require]).

Perhaps there should be an additional requirement that if the function type is noexcept, then noexcept(INVOKE(...)) must be true as well. Nonetheless, this wording is not present in the current draft.

In P0012R1, there is a comment that:

It is an open issue how to propagate "noexcept" through std::function.

My guess is that they mean that it is not clear how std::function could be implemented if this additional requirement were imposed. Hopefully, someone else can provide more details.

Community
  • 1
  • 1
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • 1
    Clearly we need a `std::function_moveonly` and `std::function_noexceptonly` and `std::function_moveonly_noexceptonly`. But what about `std::function_mutablecall`? This could get messy. – Yakk - Adam Nevraumont Aug 04 '16 at 15:02
  • Just commenting to see if people have more up to date observations on this, without making a new question – lurscher Aug 05 '21 at 01:30