【问题标题】:What is the "right" way to do this partial function application in C++ without a macro?在没有宏的情况下在 C++ 中执行此部分函数应用程序的“正确”方法是什么?
【发布时间】:2020-11-14 09:57:32
【问题描述】:

作为一个简化的示例,假设我从以下代码开始:

void function1 (Object &myObject) {
    for (int b = 0; b < 10000; b++) {
        // ... a bunch of code that uses the below myFunction(int, int) in different ways
        auto x = myObject.myFunction(12345, b);
        // do something with x and etc
    }
}

我想要做的是获取 for 循环中的内容,将其放入一个单独的内联函数中,然后以某种方式授予该函数访问 myObject.myFunction(a, b) 的柯里化版本的权限,其中 b 已经存在设置。

这样做的一种方法是:

__attribute__((always_inline))
inline void function2 (Object &myObject, int &b) {
#define curried(a) myObject.myFunction(a, b)
    // ... a bunch of code that uses the below function in different ways
    auto x = curried(12345);
    // do something with x and etc
}

void function1 (Object &myObject) {
    for (int b = 0; b < 10000; b++) {
        function2(myObject, b);
    }
}

如果您可以让编译器成功内联function2,则生成相同的汇编代码而没有开销。 (即使使用了__attribute__((always_inline)),这可能至少需要-O1 或-O2,至少使用clang。)

无论如何,在这种情况下使用宏有点尴尬,并且必须将b 的值从function1 传递给function2。将 lambda 函数作为参数传递给 function2 会更好,但后来我遇到了捕获问题(您只能传递未捕获的 lambda,但我们需要捕获 myObject) ,以及一些额外的开销。例如,我想这样做:

__attribute__((always_inline))
inline void function2 (some_type curried) {
    // ... a bunch of code that uses the below function in different ways
    auto x = curried(12345);
    // do something with x and etc
}

void function1 (Object &myObject) {
    for (int b = 0; b < 10000; b++) {
        auto lambda = [&](int a) {return myObject.myFunction(a, n);};
        function2(lambda);
    }
}

但这会失败,除非 lambda 没有捕获,因为没有适合“some_type”的类型。

所以我的问题:

是否有任何正确的方法来做到这一点,而且开销为零,至少进行了一些最小的优化?

【问题讨论】:

  • 为什么不使用 lambda:auto curried = [&amp;](auto a) { return myObject.myFunction(a, b);}
  • 您是否在优化开启的情况下进行编译?
  • inline 只是一个建议。由编译器决定它是否应该实际内联调用。'
  • 你的内联与function2有关,它对myObject.myFunction(12345, b)的调用没有影响。实际上,您只是添加了编译器可能无法完全优化的另一层 - 而不是直接调用它 - 这可能是增加开销的原因

标签: c++ optimization lambda c++17 currying


【解决方案1】:

一个 lambda 应该可以完成这项工作

    auto curried = [&](auto& a) { return myObject.myFunction(a, b);}

    auto curried = [&](const auto& a) { return myObject.myFunction(a, b);}

您是否使用其中一个取决于您的函数是否应该将a 修改为输入输出参数。如果 myObjectbcurried 之前超出范围,您应该小心,因为 lambda 将包含一个悬空引用。

还要注意 lambda 是 only available since C++11

【讨论】:

  • 我是将 lambda 传递给 function2 还是仍然传递 b 并只是替换#define?
  • @MikeBattaglia 你调用 lambda,就像你对任何函数所做的那样auto x = curried(1234);
  • 我的意思是,您只是将function2 中的#define 替换为lambda,还是更改function2 的签名以将lambda 作为参数?
  • 我认为我的问题是,要执行我想要的操作,您需要将 lambda 作为函数参数传递给 function2。但是,如果 lambda 有任何捕获,则不能这样做,因为它不能转换为函数指针,因为它具有奇怪的临时数据类型。但是,如果您传递给的函数被声明为模板,那么您可以执行此操作,这对我来说是这样做的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-18
  • 2021-08-23
  • 2017-05-08
  • 2011-03-26
  • 2021-10-22
相关资源
最近更新 更多