P0292R1 constexpr if has been included, on track for C++17. It seems useful (and can replace use of SFINAE), but a comment regarding static_assert
being ill-formed, no diagnostic required in the false branch scares me:
Disarming static_assert declarations in the non-taken branch of a
constexpr if is not proposed.
void f() {
if constexpr (false)
static_assert(false); // ill-formed
}
template<class T>
void g() {
if constexpr (false)
static_assert(false); // ill-formed; no
// diagnostic required for template definition
}
I take it that it's completely forbidden to use static_assert
inside constexpr if (at least the false / non-taken branch, but that in practice means it's not a safe or useful thing to do).
How does this come about from the standard text? I find no mentioning of static_assert
in the proposal wording, and C++14 constexpr functions do allow static_assert
(details at cppreference: constexpr).
Is it hiding in this new sentence (after 6.4.1) ? :
When a constexpr if statement appears in a templated entity, during an instantiation of the enclosing template or generic lambda, a discarded statement is not instantiated.
From there on, I assume that it is also forbidden, no diagnostic required, to call other constexpr (template) functions which somewhere down the call graph may call static_assert
.
Bottom line:
If my understanding is correct, doesn't that put a quite hard limit on the safety and usefulness of constexpr if
as we would have to know (from documentation or code inspection) about any use of static_assert
? Are my worries misplaced?
Update:
This code compiles without warning (clang head 3.9.0) but is to my understanding ill-formed, no diagnostic required. Valid or not?
template< typename T>
constexpr void other_library_foo(){
static_assert(std::is_same<T,int>::value);
}
template<class T>
void g() {
if constexpr (false)
other_library_foo<T>();
}
int main(){
g<float>();
g<int>();
}
This is talking about a well-established rule for templates - the same rule that allows compilers to diagnose
template<class> void f() { return 1; }
. [temp.res]/8 with the new change bolded:No valid specialization can be generated for a template containing
static_assert
whose condition is nondependent and evaluates tofalse
, so the program is ill-formed NDR.static_assert
s with a dependent condition that can evaluate totrue
for at least one type are not affected.Edit: I'm keeping this self-answer with examples and more detailed explanations of the misunderstandings that lead to this questions. The short answer by T.C. is strictly enough.
After rereading the proposal and on
static_assert
in the current draft, and I conclude that my worries were misguided. First of all, the emphasis here should be on template definition.If a template is instantiated, any
static_assert
fire as expected. This presumably plays well with the statement I quoted:This is a bit vague to me, but I conclude that it means that templates occurring in the discarded statement will not be instantiated. Other code however must be syntactically valid. A
static_assert(F)
, [where F is false, either literally or a constexpr value] inside a discardedif constexpr
clause will thus still 'bite' when the template containing thestatic_assert
is instantiated. Or (not required, at the mercy of the compiler) already at declaration if it's known to always be false.Examples: (live demo)
The standard text on
static_assert
is remarkably short. In standardese, it's a way to make the program ill-formed with diagnostic (as @immibis also pointed out):