The C++ standard does not explicitly disallow most ill-formed constructs, including this one. It is enough for it to not be explicitly or implicitly allowed to be ill-formed.
A conforming C++ implementation is allowed to accept anything at all, including ill-formed C++ programs. It is, however, require to issue at least one diagnostic if the input is not a well-formed C++ program (except in cases where an explicit "no diagnostic required" clause is present in the standard). So the question of what a conforming compiler is allowed to do rests on the question of whether the program in question is well-formed.
In order to address this latter question, we should establish whether constexpr
bears any relevance. The answer to this is a definite "no". The constexpr
keyword causes certain expressions that are otherwise not constant-expressions to become such, and has no other meaning. In turn, constant-expressions are different from plain vanilla expressions in a strictly limited number of well-defined contexts, such as array declarations or template instantiations. In such contexts a non-constant expression would render the program ill-formed. Since no such context is present in the program in question, we must conclude that constexpr
bears no relevance on well-formedness of the program.
In particular, being a constant-expression does not free an expression from its obligation to be type-correct. As a trivial example,
`(int*)nullptr - (int*)nullptr`
is well-formed, while
`(int*)nullptr - (double*)nullptr`
is type-incorrect and thus ill-formed, despite both operands being constant expressions, known at compile-time to be equal to nullptr
.
Disclaimer: C-style casts are for demonstration purpose only, as they read better in short code snippets. Don't use them in production code.
So we have to examine whether the program sans constexpr
is well-formed or not. This question, surprisingly, doesn't have a direct answer in the standard, and may in fact have no answer at all. According to the statically-typed nature of C++, the call should be ill-formed, but I could not find a direct statement to this effect. Rather than pondering potential implications of this, I would rather declare this a technical defect of the standard and call it a day.
The standard does indirectly state that default function arguments are properties of function declarations rather than functions themselves:
(8.3.6/4) Declarations in different scopes have completely distinct sets of default arguments
Thus, a function pointer, being an entity that references a function rather than a function declaration, should not have default arguments applied.