gpt4 book ai didi

c++ - 使编译器在类型删除中使用 lambda 优化函数调用间接

转载 作者:行者123 更新时间:2023-12-03 18:47:46 32 4
gpt4 key购买 nike

我正在使用类型删除来获取任何 class full带有成员函数 void work(char&)带有 class erased 的类型删除句柄.

// erase.hxx
#pragma once
#include <memory>

struct erased
{
private:
using fn_t = void(*)(void*, char&);
void* self;
fn_t fn;

public:
template<typename F>
explicit
erased(F& full) noexcept
: self(std::addressof(full)),
fn([](void* self, char& c) { static_cast<F*>(self)->work(c); })
{}

void work(char& c) { fn(self, c); }
};

// full.hxx
#pragma once

struct full
{
void work(char&);
};

// full.cxx
#include "full.hxx"
#include <cstdio>

// Implemented here to prevent inlining.
void full::work(char&) { puts("Working hard!"); }

// main.cxx
#include "erased.hxx"
#include "full.hxx"

template erased::erased(full&);

int main()
{
char c;
auto x = full{};
auto ex = erased{x};
ex.work(c);
}
在查看生成的程序集(GCC 10.2.0 和 Clang 11.1.0 at -O3)时,我的问题出现了:
0000000000001190 <erased::erased<full>(full&)>:
1190: 48 89 37 mov QWORD PTR [rdi],rsi
1193: 48 8d 05 06 00 00 00 lea rax,[rip+0x6] # 11a0 <erased::erased<full>(full&)::{lambda(void*, char&)#1}::__invoke(void*, char&)>
119a: 48 89 47 08 mov QWORD PTR [rdi+0x8],rax
119e: c3 ret
119f: 90 nop

00000000000011a0 <erased::erased<full>(full&)::{lambda(void*, char&)#1}::__invoke(void*, char&)>:
11a0: e9 0b 00 00 00 jmp 11b0 <full::work(char&)>
11a5: 66 2e 0f 1f 84 00 00 cs nop WORD PTR [rax+rax*1+0x0]
11ac: 00 00 00
11af: 90 nop

字段 erased::fn指向 erased::construct 中创建的 lambda ,并且这个 lambda 的主体除了立即将控制权让给 full::work 之外什么都不做。 .
由于 lambda 和 full::work似乎是二进制兼容的 我希望编译器取消 lambda 并直接存储 full::work 的地址在 erased::fn ,消除不必要的间接。
所以我的问题是:
  • 为什么编译器没有这样做?更重要的是,
  • 我怎样才能告诉编译器这样做?

  • 编辑
    我更改了 struct erase 的实现让任何时候都无法调用 erased::fn任何不是指向同一类型的指针 F static_cast<F*> 中使用的在 lambda 。
    即使没有,我怀疑假设 void* self传递给 lambda 始终是指向 F 的指针因为 F::work在其上调用 this :

    Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior.


    此外,我通过将函数类型更改为 using fn_t = void(*)(void*,char&) 使示例更加真实。 : 除了 this 指针之外,它现在还需要一个参数。
    这是为了说明,即使在上面的示例中,我所询问的优化应该是可能的,但当 F::work有签名 void work(char) : c 的拷贝必须制作:lambda 的主体将不再仅由 jmp 组成.
    我更喜欢两种情况都有效且编译器决定优化是否可行的解决方案。
    否则,我知道我可以强制参数类型与此完全匹配:
    template<typename M, typename... Args>
    struct method_with_args : std::false_type {};

    template<typename F, typename R, typename... Args>
    struct method_with_args<R(F::*)(Args...), Args...>
    : std::true_type{};

    最佳答案

    (也许我不在这里,但是)“lambda 和 full::work 似乎是二进制兼容的”并不是真的。成员函数有一个隐藏的第一个参数:指向成员对象的指针(此处为:F* this)。
    指向 F::work 的函数指针因此将是 void(F::*)(void) .这与 void(*)(void*) 不同。 ,因此不能这样替换。
    也许 this FAQ on isocpp将提供一些见解。

    关于c++ - 使编译器在类型删除中使用 lambda 优化函数调用间接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67574363/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com