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 ...
Here's a workaround from cppreference.com, i.e. use a type-dependent expression instead.
Note: the discarded statement can't be ill-formed for every possible specialization:
The common workaround for such a catch-all statement is a type-dependent expression that is always false:
e.g.
template<class T> struct dependent_false : std::false_type {};
then
static_assert(dependent_false<T>::value);
taking a slightly different tack...
#include <ciso646>
template<auto x> void something();
template<class...Conditions>
constexpr int which(Conditions... cond)
{
int sel = 0;
bool found = false;
auto elect = [&found, &sel](auto cond)
{
if (not found)
{
if (cond)
{
found = true;
}
else
{
++sel;
}
}
};
(elect(cond), ...);
if (not found) throw "you have a logic error";
return sel;
}
template<bool condition1, bool condition2, bool condition3>
void foo()
{
auto constexpr sel = which(condition1, condition2, condition3);
switch(sel)
{
case 0:
something<1>();
break;
case 1:
something<2>();
break;
case 2:
something<3>();
break;
}
}
int main()
{
foo<false, true, false>();
// foo<false, false, false>(); // fails to compile
}
As I understand it, which
is evaluated in constexpr context, which means that it is legal unless the program has to follow a code path that is illegal in a constexpr context.
For all expected cases, the throw
path is not taken, so the function is legal. When illegal inputs are provided, we go down the ill-formed path, which causes a compiler error.
I'd be interested to know whether this solution is strictly correct from a language-lawyer perspective.
It works on gcc, clang and MSVC.
...or for fans of obfuscated code...
template<class...Conditions>
constexpr int which(Conditions... cond)
{
auto sel = 0;
((cond or (++sel, false)) or ...) or (throw "program is ill-formed", false);
return sel;
}