This snippet successfully compiles using gcc6 up to gcc8. When building using gcc 9.3.0 there is an error template<typename R, typename... Types> using Function = R(*)(Types...); struct Struct { void *addr; template<typename R, typename... Types> constexpr Struct(Function<R, Types...> addr) : addr((void*)(addr)) {} }; void TestFunction() { } int main(void) { static constexpr Struct functions[]{ {TestFunction}, }; } The error is 4.cpp: In function 'int main()': 4.cpp:20:3: error: 'constexpr Struct::Struct(Function<R, Types ...>) [with R = void; Types = {}; Function<R, Types ...> = void (*)()]' called in a constant expression 20 | }; | ^ 4.cpp:9:13: note: 'constexpr Struct::Struct(Function<R, Types ...>) [with R = void; Types = {}; Function<R, Types ...> = void (*)()]' is not usable as a 'constexpr' function because: 9 | constexpr Struct(Function<R, Types...> addr) : addr((void*)(addr)) {} The full product code https://github.com/TES3MP/openmw-tes3mp/blob/0.7.1/apps/openmw-mp/Script/ScriptFunctions.hpp#L120 https://github.com/TES3MP/openmw-tes3mp/blob/0.7.1/apps/openmw-mp/Script/Types.hpp#L99
Confirmed, we used to accept this. Started with r9-6136-g43574e4ff2afd4a2e47c179921a9b5661786ebf3
Interesting, the standard doesn't actually seem to specify anything about casting a function pointer to pointer to void, which is explicitly not an object pointer under http://eel.is/c++draft/basic.compound#3: [ Note: A pointer to void does not have a pointer-to-object type, however, because void is not an object type. — end note ] The only thing it says about casting between function and non-function pointers is http://eel.is/c++draft/expr.reinterpret.cast#8: Converting a function pointer to an object pointer type or vice versa is conditionally-supported. We currently treat the C-style cast as a reinterpret_cast, and therefore reject it in a constant expression; GCC 9 is better about rejecting reinterpret_cast than earlier versions that wrongly allowed them. Curiously, clang rejects a static_cast from function pointer to void*, allows a reinterpret_cast, rejects the reinterpret_cast in a constant expression, but allows a C-style cast in a constant expression. That seems inconsistent. Suspending pending feedback from the C++ committee.
(In reply to Jason Merrill from comment #2) > Interesting, the standard doesn't actually seem to specify anything about > casting a function pointer to pointer to void, which is explicitly not an > object pointer under http://eel.is/c++draft/basic.compound#3: > > [ Note: A pointer to void does not have a pointer-to-object type, however, > because void is not an object type. — end note ] I was misreading this passage; pointer to void is an object pointer type even though it is not a pointer-to-object type. "The type of a pointer to cv void or a pointer to an object type is called an object pointer type." > The only thing it says about casting between function and non-function > pointers is http://eel.is/c++draft/expr.reinterpret.cast#8: > Converting a function pointer to an object pointer type or vice versa is > conditionally-supported. So this passage covers this case. > We currently treat the C-style cast as a reinterpret_cast, and therefore > reject it in a constant expression; GCC 9 is better about rejecting > reinterpret_cast than earlier versions that wrongly allowed them. And this is correct. > Curiously, clang rejects a static_cast from function pointer to void*, > allows a reinterpret_cast, rejects the reinterpret_cast in a constant > expression, but allows a C-style cast in a constant expression. That seems > inconsistent. Richard Smith from the clang team agrees that this is a clang bug. So, your testcase is ill-formed.