编译器无法执行 constexpr 表达式

emi*_*k_g 4 c++ templates constexpr c++17 constexpr-function

我有这样的代码:

template<typename ... Args>
constexpr size_t get_init_size(Args ... args) {
    return sizeof...(Args);
}

template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
    constexpr size_t header_lenght = get_init_size(args...);
    return header_lenght;
}

constexpr auto create_ipv4_header() {
    constexpr auto x = make_generic_header(0b01, 0b10, 0b01);
    return x;
}
Run Code Online (Sandbox Code Playgroud)

我知道这是虚拟代码,但我将其隔离以查找错误。

编译器给我错误(GCC):

In instantiation of 'constexpr auto make_generic_header(Args&& ...) [with Args = {int, int, int}]':
/tmp/tmp.CaO5YHcqd8/network.h:39:43:   required from here
/tmp/tmp.CaO5YHcqd8/network.h:31:22: error: 'args#0' is not a constant expression
   31 |     constexpr size_t header_lenght = get_init_size(args...);
      |                      ^~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

我尝试将限定符const添加到函数参数,但它同样不起作用。理论上所有这些函数都可以在编译时计算。但以我的知识,我找不到问题在哪里。

chr*_*ris 5

constexpr变量与函数的含义不同。

\n

对于变量来说,这意味着该变量必须是编译时的。因此,需要用常量表达式进行初始化。

\n

对于函数来说,constexpr意味着除了运行时之外,函数还可以使用内部相同的代码在编译时运行。因此,您在其中所做的一切都必须适用于运行时调用。

\n

考虑到这一点,让我们研究一下make_generic_header

\n
template<typename ... Args>\nconstexpr auto make_generic_header(Args ... args) {\n    constexpr size_t header_lenght = get_init_size(args...);\n    return header_lenght;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这里,header_lenght是一个constexpr变量,所以必须是编译时的。因此,get_init_size(args...)也必须在编译时完成。但是,由于各种原因,参数不是常量表达式,因此这行不通。如果这确实有效,则意味着make_generic_header函数constexpr在运行时\xc2\xb9 时不可用,这不符合其在编译时和运行时都可用的要求。

\n

修复方法相当简单:使用在这两种情况下都有效的代码:

\n
template<typename ... Args>\nconstexpr auto make_generic_header(Args ... args) {\n    size_t header_lenght = get_init_size(args...);\n    return header_lenght;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

你发现了吗?唯一的变化是constexpr从 中删除header_lenght。如果这个函数在编译时运行,它仍然会起作用,并且整个函数调用表达式将是一个常量表达式。

\n
\n

\xc2\xb9“但它在任何一个都不起作用consteval!” - 确实,答案中给出的推理足以满足constexpr,但我省略了更根本的原因,因为它与此处无关。

\n