【问题标题】:C++ lambda doesn't deduce function overloadingC++ lambda 不推断函数重载
【发布时间】:2020-09-27 17:53:14
【问题描述】:

我正在定义一个这样的类:

class foo {
public:
  // I define const and non-const versions of the 'visit' function
  // nb. the lambda is passed by reference
  virtual void visitWith(std::function<void(foo&)>&);
  virtual void visitWith(std::function<void(const foo&)>&) const;
};

foo 可以有孩子,所以我们的想法是递归地访问 foo 及其所有孩子。

当我尝试使用它时,例如。像这样:

foo f;
f.visitWith([&](const foo&) {
  // Do something here
});

我得到编译器错误。编译器不知道该怎么做。

我可以通过添加这样的类型转换使其工作:

foo f;
f.visitWith( (std::function<void(const foo&)>) [&](const foo&) {
  // Do something here
});

但这太可怕了。

我怎样才能让它整齐地工作?

编辑:

这可能是 Visual C++ 的问题,它拒绝编译这里给出的代码:

https://ideone.com/n9bySW

我尝试编译时的 VC++ 输出是:

Edit2:不,Visual C++ 是正确的,代码是模棱两可的。请参阅下面的解决方案...

【问题讨论】:

  • 这是just a typo,因为问题不在于 lambda 的参数,而在于 lambda 的传递方式。

标签: c++ lambda overloading


【解决方案1】:

lambda 是编译器生成的类型,它不是std::function 的实例,但它可以分配给一个。

您的visitWith() 方法通过非常量引用获取std::function,这意味着它需要一个预先存在的std::function 对象,例如:

std::function<void(const foo&)> func = [&](const foo&) {
    // Do something here
};
foo f;
f.visitWith(func);

将 lambda 直接传递给 visitWith() 需要编译器创建一个临时的 std::function 对象,但非常量引用不能绑定到临时对象。这就是您的原始代码无法编译的原因。

对于您正在尝试的内容,您必须通过值或 const-reference 来传递 std::function

class foo {
public:
    void visitWith(std::function<void(foo&)>);
    void visitWith(std::function<void(const foo&)>) const;
};

Live Demo

class foo {
public:
    void visitWith(const std::function<void(foo&)> &);
    void visitWith(const std::function<void(const foo&)> &) const;
};

Live Demo

【讨论】:

  • 我是否认为按值传递的 lambda 会在每个函数调用上复制该对象,这可能很昂贵? (取决于 lambda 捕获的内容),还是 lambda 更像是廉价复制函数指针?
  • 按值传递的 lambda 确实会复制,是的。将 lambda 视为实现了 operator() 的匿名结构。但是您的 visitWith 并没有采用/传递 lambda 本身(visitWith 必须采用模板参数才能做到这一点),它正在采用/传递引用 lambda 的 std::function。见stackoverflow.com/questions/8711391
  • 这可能是 Visual C++ 的问题,它拒绝编译下面显示的代码,即使它在该网站上编译和运行:ideone.com/n9bySW
  • 编辑:在初始问题中添加了编译器错误的图像^^。
【解决方案2】:

我向微软报告了这个“错误”并得到了回复,这里:

https://developercommunity.visualstudio.com/content/problem/1201858/c-stdfunction-overloading-fails.html

短版:Visual C++ 处理正确,ideone 错误。

最后我通过向 foo 添加第三个重载来解决它,它可以向对象添加 const-ness,如下所示:

class foo {
public:
  // Use typedefs so that all the code that comes after these two functions is neater
  typedef std::function<void(Branch&)>visitor;
  typedef std::function<void(const Branch&)>const_visitor;
  
  virtual void visitWith(const visitor&);
  virtual void visitWith(const const_visitor&) const;

  // This is to thunk the third case that can happen when you start
  // a const visit from a non-const foo.
  void visitWith(const const_visitor& v) {
    static_cast<const foo*>(this)->visitWith(v);  // Add const-ness
  }

};

现在代码可以工作了,例如:

foo f;
f.visitWith([](const foo& f) {
    std::cout << "visited a const foo!" << std::endl;
});

【讨论】:

  • 这真的和你的原始代码没有什么不同,只是使用了typedef,这不是必需的。请参阅我刚刚添加到答案中的演示链接,其中显示了我的示例有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-17
相关资源
最近更新 更多