如何断言 constexpr if else 子句永远不会发生?

问题描述 投票:0回答:4

如果条件为真,我想在非 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);
c++ c++17
4个回答
54
投票

您必须使废弃的语句依赖于模板参数

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
语句的子语句生成有效的专业化,并且模板未实例化,或者...


26
投票

这是来自 cppreference.com 的解决方法,即使用依赖于类型的表达式。

注意:对于每个可能的专业化,被丢弃的语句都不能是格式错误的:

此类包罗万象的语句的常见解决方法是始终为 false 的类型相关表达式:

例如

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

然后

static_assert(dependent_false<T>::value);

9
投票

使用 C++23,这个问题的答案发生了变化。论文P2593:允许 static_assert(false) 已在 2023-02 Issaquah 会议 上被接受。我们现在可以使用OP想要使用的习语。

我们可以从接受的措辞中看到,dcl.pre p10已被修改,因此

static_assert
模板定义上下文中不起作用,并且temp.res p6已被修改,因此不再如果 static_assert
 所有专业都失败,则格式错误,无需诊断

还添加了以下示例:

template <class T>
  void f(T t) {
    if constexpr (sizeof(T) == sizeof(int)) {
      use(t);
    } else {
      static_assert(false, "must be int-sized");
    }
  }
  
  void g(char c) {
    f(0); // OK
    f(c); // error: must be int-sized
  }
  

4
投票

采取稍微不同的策略...

#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;
}
© www.soinside.com 2019 - 2024. All rights reserved.