Den*_*dir 1 c++ g++ compiler-optimization clang++ c++20
我尝试了一下constexpr并发现了一些有趣的行为:
constexpr在函数前面添加使GCC能够尝试更努力地优化,从而导致完全优化函数并仅提供计算值。constexpr会导致错误,因为它内部使用了未标记的constexpr(特别是)函数/内部函数(编译器内置memcpy)。constexprconstexpr为什么会这样?
constexpr?memcpy constexpr(请参阅原始修订版中的第III.B节),但这被拒绝并更改,因为此类函数的编译器内置版本将实现相同的功能(请参阅最新修订版中的第 III.A节)。memcpy在constexpr函数/上下文中使用是错误的吗?(注:memcpy和__builtin_memcpy是等效的。)为了更容易理解,这里举个例子。(您甚至可以在此处的Compiler Explorer
中更轻松地查看其结果。)
注意:我无法想出一个简单的例子,其中简单地添加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;
}
Run Code Online (Sandbox Code Playgroud)
GCC能够将其优化为:
main: # @main
mov eax, -1
ret
Run Code Online (Sandbox Code Playgroud)
如果删除constexpr函数定义前面的, Clang也可以对其进行优化。
但是,如果在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
Run Code Online (Sandbox Code Playgroud)
铛:
<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
Run Code Online (Sandbox Code Playgroud)
对于既不是默认值也不是模板的 constexpr 函数或 constexpr 构造函数,如果不存在参数值,则函数或构造函数的调用可以是核心常量表达式的计算子表达式,或者对于构造函数,可以是某些常量初始化对象([basic.start.static])的初始化完整表达式,程序格式错误,无需诊断。
否则,您的程序的 NDR 格式不正确memcpy。constexpr
在上下文中使用该函数contsexpr可以进行诊断。
在某些情况下,
constexpr在函数前面添加使 GCC 能够尝试更努力地优化,从而导致完全优化函数并仅提供计算值。
这是一个很好的提示(和inline以前一样)。
constexpr函数可能被“滥用”:
constexpr std::size_t factorial(std::size_t n) {/*..*/}
int main()
{
std::cout << factorial(5); // computed at runtime (but probably optimized)
}
Run Code Online (Sandbox Code Playgroud)
正确的方法是
int main()
{
constexpr auto fact5 = factorial(5); // computed at compile time
std::cout << fact5;
}
Run Code Online (Sandbox Code Playgroud)