How to cause static error in constexpr if-else chain?

Viewed 149

In the following C++20 function template:

template<int i>
void f() {
    if constexpr (i == 1)
       g();
    else if constexpr (i == 2)
       h();
    else
       ??? // <--error
}

Is there something we can write in ??? such that a call of f<3>() will fail at compile-time?

4 Answers

The problem is that the discarded statement of constexpr if can't be ill-formed for every possible specialization. [temp.res.general]/6

(emphasis mine)

The validity of a template may be checked prior to any instantiation.

The program is ill-formed, no diagnostic required, if:

  • no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or

You can use a type-dependent expression that is always false. E.g.

template<int i> struct dependent_false : std::false_type {};

template<int i>
void f() {
    if constexpr (i == 1)
       g();
    else if constexpr (i == 2)
       h();
    else
       static_assert(dependent_false<i>::value, "Must be 1 or 2");
}

The standard idiom for this is to have a dependent template, that is specialized to std::false_type, like this:

template<int T> struct dependent_false : std::false_type {};

and then you can do:

template<int i>
void f() {
    if constexpr (i == 1)
       g();
    else if constexpr (i == 2)
       h();
    else
       static_assert(dependent_false<i>::value, "i can only be 1 or 2"); 
}

The reason you can't just say

static_assert(false, "i can only be 1 or 2");

is a rule in the language that says a branch of an if constexpr can't be false for every possible instantiation of the enclosing template.

Adding a template that could be specialized for std::true_type gets around this limitation.

For the reasons given in the other answers, I would rewrite in the following way:

template<int i>
void f() {
    static_assert(i == 1 || i == 2, "must be 1 or 2");
    if constexpr (i == 1)
       g();
    if constexpr (i == 2)
       h();
}

In this way, you can avoid the class template.

Just write

template<int i>
void f() {
    if constexpr (i == 1)
       g();
    else {
       static_assert(i == 2);
       h();
    }
}

which requires rewriting only the last condition.

Related