Maestro
Maestro

Reputation: 2572

Can a non-throwing function pointer point to a throwing-function?

On C++ Primer on noexcept exception specification, it is said that a pointer to a function that may throw Implicitly (defined without exception specification e.g: void(*p)();) or explicitly (void(*p)() noexcept(false);) may point to any function even to a non-throwing function.

On the other hand a function pointer which may not throw (noexcept e.g void(*p) noexcept;) can only point to a function that won't throw.

I found that very logical because the first pointer it is OK to point to a non-throwing function from a throwing function pointer and the second too is so logical.

I've tried this to understand more:

void func1(){ // may throw
    std::cout << "func1()\n";
}

void func2() noexcept(false){ // may throw
    std::cout << "func2()\n";
}

void func3() noexcept(true){ // won't throw
    std::cout << "func3()\n";
}

void func4() noexcept{ // won't throw
    std::cout << "func4()\n";
}


int main(int argc, char* argv[]){

    void(*pFn1)();
    pFn1 = func1; // OK
    pFn1 = func2; // OK
    pFn1 = func3; // OK
    pFn1 = func4; // OK

    void(*pFn2)() noexcept(false);
    pFn2 = func1; // OK
    pFn2 = func2; // OK
    pFn2 = func3; // OK
    pFn2 = func4; // OK

    void(*pFn3)() noexcept(true);
    pFn3 = func1; // Error on C++ 17 and above. OK on C++11 and 14
    pFn3 = func2; // Error on C++ 17 and above. OK on C++11 and 14
    pFn3 = func3; // OK
    pFn3 = func4; // OK

    void(*pFn4)() noexcept(true);
    pFn4 = func1; // Error on C++ 17 and above. OK on C++11 and 14
    pFn4 = func2; // Error on C++ 17 and above. OK on C++11 and 14
    pFn4 = func3; // OK
    pFn4 = func4; // OK

    std::cout << '\n';
}

Does this mean the standard has changed? Thank you!

Upvotes: 7

Views: 262

Answers (1)

Jonathan Wakely
Jonathan Wakely

Reputation: 171393

It's ill-formed in C++17 because the noexcept is part of the type system, and there is no conversion from a pointer to a potentially-throwing function to a pointer to a non-throwing function.

Before C++17 the relevant rule is [except.spec] p5:

A similar restriction applies to assignment to and initialization of pointers to functions, pointers to member functions, and references to functions: the target entity shall allow at least the exceptions allowed by the source value in the assignment or initialization.

So although the noexcept isn't part of the type system, the OP's assignments are ill-formed.

G++ does not give an error for the example in p5:

class A { /* ... */ };
void (*pf1)(); // no exception specification
void (*pf2)() throw(A);

void f() {
  pf1 = pf2; // OK: pf1 is less restrictive
  pf2 = pf1; // error: pf2 is more restrictive
}

That should be ill-formed in C++98/11/14.

The equivalent with a noexcept-specifier would be:

class A { /* ... */ };
void (*pf1)(); // no exception specification
void (*pf2)() noexcept;

void f() {
  pf1 = pf2; // OK: pf1 is less restrictive
  pf2 = pf1; // error: pf2 is more restrictive
}

G++ doesn't reject this in 98/11/14, only C++17 and up.

Upvotes: 2

Related Questions