[expr.call]/6:
Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior.
void f() noexcept {}; // function type is "noexcept function"
void (*pf)() = f; // variable type is "pointer to function"; initialized by result of [conv.fctptr]([conv.func](f))
int main()
{
(*pf)(); // `*pf`: lvalue expression's function type is "function" (without noexcept!)
}
Does the above call result in undefined behavior per the cited standardese?
C++14 had a weaker requirement, from [expr.call]/6:
[...] Calling a function through an expression whose function type has a language linkage that is different from the language linkage of the function type of the called function's definition is undefined ([dcl.link]). [...]
However [expr.reinterpret.cast]/6 contained a similar, but stronger requirement:
A function pointer can be explicitly converted to a function pointer of a different type. The effect of calling a function through a pointer to a function type ([dcl.fct]) that is not the same as the type used in the definition of the function is undefined.
P0012R1 made exception specifications to be part of the type system, and was implemented for C++17
The exception specification of a function is now part of the function’s type:
void f() noexcept(true);
andvoid f() noexcept(false);
are functions of two distinct types. Function pointers are convertible in the sensible direction. (But the two functionsf
may not form an overload set.) This change strengthens the type system, e.g. by allowing APIs to require non-throwing callbacks.
and moreover added [conv.fctptr]:
Add a new section after section 4.11 [conv.mem]:
4.12 [conv.fctptr] Function pointer conversions
A prvalue of type "pointer to noexcept function" can be converted to a prvalue of type "pointer to function". [...]
but included no changes to [expr.reinterpret.cast]/6; arguably an unintentional omission.
CWG 2215 highlighted the duplicated information in [expr.call] compared to [expr.reinterpret.cast]/6, flagging the weaker requirement in the former as redundant. The following cplusplus / draft
commit implemented CWG 2215, and removed the weaker (redundant) requirement, made [expr.reinterpret.cast]/6 into a non-normative note and moved its (stronger) normative requirement to [expr.call]; eventually this stronger requirement was broken out into its own paragraph.
This confusion arguably lead to the unintentional (seemingly conflicting) rules that:
noexcept
function” can be converted to a prvalue of type “pointer to function” ([conv.fctptr]/1), andAfaict, there are no defect reports covering this issue, and a new one should arguably be submitted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With