Gear Gear - 4 months ago 27
C++ Question

How will C++17 exception specifier type system work?

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?

Answer

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.