为什么编译器不会在我的 lambda 中内联常量捕获?

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

给出定义:

#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); }

我希望优化编译器能够返回一个循环展开/消除的函数。 然而

gcc、clang
、msvc 和 icc 都创建带有循环的函数定义: oneCall

事实上,涉及捕获常量(例如两个常量之和)的表达式似乎都是内联的。定义中是否有某些内容阻止内联? (因为所有编译器似乎都同意。)

如何定义

.L3: call call() addq $1, %rbx cmpq 0(%rbp), %rbx jne .L3

以便调用者可以内联捕获的常量? (即展开循环 iff 使用编译时间常数

n
调用) 手动将 lambda 定义移至

nWrap

确实

消除了生成代码中的循环
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
影响不大。
生产代码实际上将调用者提供的 lambda(或可调用)包装在 

constexpr

中。 调用者 lambda

 是内联的 
。然而,如果不定义两个单独的 lambda 函数,我找不到一种方法来消除常见单调用用例的循环。

c++ lambda compiler-optimization inlining
1个回答
0
投票
解释

std::function这里无法产生优化的返回值的原因是因为它会改变(非模板化)函数的返回

type
这样,我发现将

nWrap

返回值嵌入到

另一个 lambda 定义
(因此是一个不同的类型),将使编译器能够消除循环: nCalls

中的结果始终返回优化的

static inline constexpr auto nWrap(std::size_t n) {
    return [n]() { for (std::size_t i = 0; i != n; ++i) call(); };
};

std::function<void()> nCalls(std::size_t n) {
    if (n == 1) {
        return []{nWrap(1)();};  // Return value embedded into another lambda
    }
    return nWrap(n);
}

std::function<void()> oneCall() { return nCalls(1); }
oneCall
在运行时决定返回什么——这
不是我要求的,
但这是我目前正在解决的问题。

© www.soinside.com 2019 - 2024. All rights reserved.