为何编译器无法内联Lambda中捕获的常量?
问题分析与优化需求
原始代码实现
#include <functional> void call(); static constexpr inline auto nWrap(std::size_t n) { return [n]() constexpr { for (std::size_t i = 0; i != n; ++i) call(); }; } std::function<void()> oneCall() { return nWrap(1); } std::function<void()> nCalls(std::size_t n) { return nWrap(n); }
编译器生成的未优化代码
原本期望oneCall返回的可调用对象能被编译器优化掉循环(展开或直接消除),但GCC、Clang、MSVC、ICC这几款编译器生成的代码均保留了循环逻辑:
.L3: call call() addq $1, %rbx cmpq 0(%rbp), %rbx jne .L3
核心疑问
所有涉及捕获常量表达式(比如两个常量之和)的场景都无法被内联,是不是nWrap的定义中存在阻止内联的因素?
如何修改nWrap的定义,让它在传入编译时常量n时,能内联捕获的常量,实现循环消除/展开;同时保留对运行时变量n的支持?
手动优化验证
将lambda直接写在oneCall内部时,编译器确实成功消除了循环:
constexpr const std::size_t n = 1; return [n] { for (std::size_t i = 0; i != n; ++i) call(); };
对应的生成代码:
std::_Function_handler<void (), oneCall()::{lambda()#1}>::_M_invoke(std::_Any_data const&): jmp call()
已尝试的无效方案
- 对
nWrap进行模板化无法解决问题,除非将n作为模板参数 - 添加或移除
static、inline、constexpr修饰符几乎没有效果
实际需求
生产代码会将调用者提供的Lambda(或可调用对象)包装到std::function中,调用者的Lambda可以被内联,但目前找不到无需定义两个独立Lambda的方法,来消除单调用场景的循环。
内容的提问来源于stack exchange,提问作者yoerg
相关产品推荐
相关产品推荐

