为什么 constexpr context 使编译器失败,而 w/o 它优化完美?

Why does constexpr context make the compiler fail, while w/o it optimizes perfectly?

我尝试了 constexpr 并发现了一些有趣的行为:

为什么会这样?


例子比较容易理解,这里举这样一个例子。
(您甚至可以在 Compiler Explorer here 中更轻松地查看它的结果。)

注意: 我无法想出一个简单的例子,在函数中简单地添加 constexpr 可以帮助 GCC 优化器进行全面优化,否则它不会。但请相信我,我有这样的例子,它们更复杂(遗憾的是闭源)。

#include <array>
#include <cstdint>
#include <cstring>

constexpr std::uint32_t extract(const std::uint8_t* data) noexcept
{
    std::uint32_t num;
    memcpy(&num, data, sizeof(std::uint32_t));
    return num;
}

int main()
{
    constexpr std::array<std::uint8_t, 4> a1 {{ 0xff, 0xff, 0xff, 0xff }};
    /*constexpr*/ auto val = extract(a1.data());  // <--- Using constexpr here makes compiler fail.
    return val;
}

GCC 能够将其优化为:

main:     # @main
    mov   eax, -1
    ret

Clang也可以优化,如果去掉函数定义前面的constexpr

但是,如果在函数调用前的 constexpr 中进行注释(从而从 constexpr 上下文中调用函数),编译器将失败并显示如下内容:

海湾合作委员会:

<source>: In function 'int main()':
<source>:15:33:   in 'constexpr' expansion of 'extract(a1.std::array<unsigned char, 4>::data())'
<source>:8:11: error: 'memcpy(((void*)(& num)), ((const void*)(& a1.std::array<unsigned char, 4>::_M_elems)), 4)' is not a constant expression
    8 |     memcpy(&num, data, sizeof(std::uint32_t));
      |     ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler returned: 1

叮当声:

<source>:5:25: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
constexpr std::uint32_t extract(const std::uint8_t* data) noexcept
                        ^
<source>:8:5: note: non-constexpr function 'memcpy' cannot be used in a constant expression
    memcpy(&num, data, sizeof(std::uint32_t));
    ^
<source>:15:20: error: constexpr variable 'val' must be initialized by a constant expression
    constexpr auto val = extract(a1.data());  // <--- Error!
                   ^     ~~~~~~~~~~~~~~~~~~
<source>:8:5: note: non-constexpr function 'memcpy' cannot be used in a constant expression
    memcpy(&num, data, sizeof(std::uint32_t));
    ^
<source>:15:26: note: in call to 'extract(&a1._M_elems[0])'
    constexpr auto val = extract(a1.data());  // <--- Error!

                         ^
2 errors generated.
Compiler returned: 1

根据dcl.constexpr

For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, an evaluated subexpression of the initialization full-expression of some constant-initialized object ([basic.start.static]), the program is ill-formed, no diagnostic required.

由于 memcpy 不是 constexpr,您的程序格式错误 NDR。

使用 contsexpr 上下文中的函数将允许进行诊断。

In some situations adding constexpr in front of a function enables GCC to try optimizing harder which results in fully optimizing the function away and just providing the calculated value.

这是一个很好的提示(如之前的inline)。

constexpr函数可以是"misused":

constexpr std::size_t factorial(std::size_t n) {/*..*/}

int main()
{
    std::cout << factorial(5); // computed at runtime (but probably optimized)
}

正确的方法是

int main()
{
    constexpr auto fact5 = factorial(5); // computed at compile time
    std::cout << fact5; 
}