Как утверждать, что constexpr, если предложение else никогда не произойдет? - программирование

Как утверждать, что constexpr, если предложение else никогда не произойдет?

Я хочу вызвать ошибку времени компиляции, если не в constexpr, если условия выполняются, например:

if constexpr(condition1){
    ...
} else if constexpr (condition2) {
   ....
} else if constexpr (condition3) {
  ....
} else {
    // I want the else clause never taken. But I heard the code below is not allowed
    static_assert(false);
}

// I'd rather not repeat the conditions again like this:
static_assert(condition1 || condition2 || condition3);
4b9b3361

Ответ 1

Вы должны сделать выброшенный оператор зависимым от параметров шаблона

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>);
}

Это так потому что

[temp.res]/8 - программа некорректна, диагностика не требуется, если

Для шаблона или constexpr if нельзя создать действительную специализацию, constexpr if оператор внутри шаблона и шаблона не создан, или...

Ответ 2

Вот обходной путь от cppreference.com, т.е. используйте вместо этого зависимое от типа выражение.

Примечание: отклоненное утверждение не может быть неправильно сформировано для любой возможной специализации:

Обычный обходной путь для такого универсального оператора - это зависимое от типа выражение, которое всегда ложно:

например

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

затем

static_assert(dependent_false<T>::value);

Ответ 3

принимая немного другой курс...

#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
}

Как я понимаю, which оценивается в constexpr контексте, что означает, что он является законным, если программа не должна следовать по пути кода, который является незаконным в контексте constexpr.

Во всех ожидаемых случаях путь throw не берется, поэтому функция допустима. Когда предоставляются недопустимые входные данные, мы идем по неверно сформированному пути, который вызывает ошибку компилятора.

Мне было бы интересно узнать, является ли это решение строго правильным с точки зрения языка-юриста.

Работает на gcc, clang и MSVC.

... или для любителей запутанного кода...

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;
}