将捕获作为函数指针的 C lambda

新手上路,请多包涵

我在玩 C++ lambda 及其到函数指针的隐式转换。我的开始示例是使用它们作为 ftw 函数的回调。这按预期工作。

 #include <ftw.h>
#include <iostream>

using namespace std;

int main()
{
    auto callback = [](const char *fpath, const struct stat *sb,
        int typeflag) -> int {
        cout << fpath << endl;
        return 0;
    };

    int ret = ftw("/etc", callback, 1);

    return ret;
}

修改它以使用捕获后:

 int main()
{

    vector<string> entries;

    auto callback = [&](const char *fpath, const struct stat *sb,
        int typeflag) -> int {
        entries.push_back(fpath);
        return 0;
    };

    int ret = ftw("/etc", callback, 1);

    for (auto entry : entries ) {
        cout << entry << endl;
    }

    return ret;
}

我得到了编译器错误:

 error: cannot convert ‘main()::<lambda(const char*, const stat*, int)>’ to ‘__ftw_func_t {aka int (*)(const char*, const stat*, int)}’ for argument ‘2’ to ‘int ftw(const char*, __ftw_func_t, int)’

经过一番阅读。我了解到使用捕获的 lambda 不能隐式转换 为函数指针。

有解决方法吗?它们不能“隐式”转换的事实是否意味着它们可以“显式”转换? (我尝试铸造,但没有成功)。什么是修改工作示例的干净方法,以便我可以使用 lambdas 将条目附加到某个对象?

原文由 duncan 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.9k
2 个回答

由于捕获 lambda 需要保留状态,因此实际上并没有简单的“解决方法”,因为它们 不仅仅是 普通函数。函数指针的关键在于它指向一个单一的全局函数,并且这个信息没有状态的空间。

最接近的解决方法(基本上放弃状态)是提供某种类型的全局变量,可以从您的 lambda/函数访问。例如,您可以创建一个传统的仿函数对象并给它一个静态成员函数,该函数引用一些唯一的(全局/静态)实例。

但这有点违背了捕获 lambdas 的全部目的。

原文由 Kerrek SB 发布,翻译遵循 CC BY-SA 3.0 许可协议

@vladimir-talybin 的 回答 有点问题:

 template <class F>
auto cify_no_args(F&& f) {
  static F fn = std::forward<F>(f);
  return [] {
    return fn();
  };
}

也就是说,如果 lambda 在函数中被调用了两次,那么只有第一次调用是有效的,例如

// only a demo
void call(std::vector<int>& nums) {
  static int i = 0;
  cify_no_args([&]() {
    nums.emplace_back(i++);
  })();
}

int main() {
  std::vector<int> nums1, nums2;
  call(nums1);
  call(nums2);

  std::cout << nums1.size() << std::endl << nums2.size() << std::endl;
}

您将显示 20 的输出,这意味着 call 函数的第二次调用正在使用第一次调用的闭包。

那是因为解决方案是使用静态来存储闭包的引用,并且一旦存储了引用,它就不会改变,即使是新的闭包。如果闭包被破坏(由于超出范围或其他原因),情况会变得更糟。

我对这个问题的解决方案是简单地将引用转换为指针,并在每次我们“构造”lambda 时更新指针的值:

 template <class F>
auto cify_no_args(F&& f) {
  static typename std::remove_reference<F>::type* fn;
  fn = &f;
  return [] {
    return (*fn)();
  };
}

开销是两次内存访问,一次用于读取,一次用于写入,但确保了正确性。

原文由 byte 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题