5

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 4

8

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");
}
Sign up to request clarification or add additional context in comments.

1 Comment

CWG2518 (retroactively!) made static_assert a special case here for just this reason.
3

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.

1 Comment

Note that a DR does allow static_assert(false); now, although you still have to avoid the instantiation being invalid for all template arguments for some other reason.
3

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.

1 Comment

This is the best suggestion because it does not require additional template (dependent_false).
1

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.

6 Comments

A worthy thought, but in the real world code there are many more branches in the if-else chain and the condition expressions are more complex, and so you're suggested solution has a DRY violation in that the conditions all have to be repeated twice (once in static assert and once in condition).
@AndrewTomazos Before this sort of DRY violation becomes worrisome, I would consider whether "one f does different things for different values of i" is the right pattern.
Every if-else chain is a statement that does different things for a set of mutually exclusive conditions. ;)
As C++20, template <int i> void f() requires (i == 1) { g(); } template <int i> void f() requires (i == 2) { h(); } :-) But I would probably go for tag dispatching void f(std::integral_constant<int, 1>) { g();} template <int i> void f() { f(std::integral_constant<int, i>{}); }.
@Jarod42: Now you have a DRY violation in repeating the function template declaration template<params> R f(params) N times, rather than just once. ;)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.