How to Assert That a Constexpr If Else Clause Never Happen

How to assert that a constexpr if else clause never happen?

You have to make the discarded statement dependent of the template parameters

template <class...> constexpr std::false_type always_false{};

if constexpr(condition1){
...
} else if constexpr (condition2) {
....
} else if constexpr (condition3) {
....
} else {
static_assert(always_false<T>);
}

This is so because

[temp.res]/8 - 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 ...

constexpr if and static_assert

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:

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 ([stmt.if]) within a
    template
    and the template is not instantiated, or
  • [...]

No valid specialization can be generated for a template containing static_assert whose condition is nondependent and evaluates to false, so the program is ill-formed NDR.

static_asserts with a dependent condition that can evaluate to true for at least one type are not affected.

How to skip uncompilable code through constexpr-if in C++?

As mentioned in the comments, "you have to make the discarded statement dependent of the template parameters" - What you are trying to do is not possible without introducing templates.

For example, this would "work" but its far from a good solution. I added it as answer as the similarity to the question that I marked as a duplicate was disputed.

#include <type_traits>
#include <iostream>

int main() {
struct A { int i = 123; };
struct B { int j = 456; };
B x;

[](auto const & x) {
if constexpr(std::is_same_v<std::remove_cvref_t<decltype(x)>, A>) {
std::cout << "A: " << x.i;
} else if constexpr(std::is_same_v<std::remove_cvref_t<decltype(x)>, B>) {
std::cout << "B: " << x.j;
}
}(x);
}

Stop compilation if `if constexpr` does not match

The soltuion is to use static_assert.

But we can't simply do static_assert(false, "whatever"); in the else branch, because since the condition doesn't depend on the template parameter, assertion might fire early (when the compiler first sees your function body, even if the else branch is never actually taken).

The condition of static_assert has to somehow depend on T, to delay the assertion check until your template is instantinated.

This is what I've been using:

template <auto A, typename...> auto value = A;
if constexpr (foo)
{
...
}
else if constexpr (bar)
{
...
}
else
{
static_assert(value<false, T>, "Invalid template parameter.");
}

Note that if you only have one if constexpr (rather than an if else if chain), then none of that is needed.

Simply move the condition from if to a static_assert and remove the if.

C++ force compiler error if template resolution fails

You can add static_assert as

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

template <typename T>
const T& get() const {
if constexpr ( std::is_same_v<T, C1> )
return this->c1;
else if constexpr( std::is_same_v<T, C2> )
return this->c2;
else
static_assert(dependent_false<T>::value, "some error message");
}


Related Topics



Leave a reply



Submit