【问题标题】:Access violation on std::function assigned to a lambda分配给 lambda 的 std::function 访问冲突
【发布时间】:2018-03-11 08:57:35
【问题描述】:

我试图玩弄 std::function 和 std::bind ,但我遇到了一个问题。我想构建一个通用结构,允许我将 std::function 绑定到成员函数,而无需先验地知道成员函数的参数。我写了这个东西

template<typename Class, typename Return, typename ...Args>
struct Caller
{
private:
    std::function<Return(Args ...)> callerFunction;

    Caller(const Caller&) = delete;
    Caller(Caller&&) = delete;
    Caller& operator=(const Caller&) = delete;

public:
    ~Caller() = default;
    Caller() = default;

    Caller(Class& instance, Return(Class::*function)(Args...))
    {
        callerFunction = [&](Args... args) { return (instance.*function)(args...); };
    }

    Return operator() (Args ... args)
    {
        return callerFunction(args...);
    }
};

仅供参考,我知道函数的参数是按值传递的(我在使用带有可变参数模板的通用引用时遇到了一些问题,我稍后会处理)。

这里的问题是,当我使用 operator() 触发函数时,我得到了访问冲突错误。我试图缩小问题范围并创建一个没有可变参数的结构(允许成员函数只有一个 int 作为参数),我看到将 lambda 分配给 std::function 给了我同样的错误,但是如果我使用 std::bind 和占位符一切都很好。

试验场是这里

class A
{
public:
    bool foo(int a)
    {
        std::cout << a << std::endl;
        return true;
    }
};

int main()
{
    A a;
    a.foo(9);

    Caller<A, bool, int> caller(a, &A::foo);
    caller(10);

    std::cin.ignore();
}

使用 lambda,是否需要保存类的实例才能正确调用成员函数?

【问题讨论】:

  • 我很确定你有 UB。在Caller 的构造函数中,您按值传递成员函数指针(因此将创建仅存在于构造函数内部的指针副本),然后通过引用在 lambda 中捕获它。因此,当您调用 caller(10) 时,指针已超出范围。 instance 在此示例中很好,因为您通过引用将其传递给构造函数,并且当您调用 caller(10) 时,a 仍在范围内。 (尽管您应该假设将来会发生变化并围绕它进行设计。)
  • 还值得注意的是,已经存在一个 std::mem_fn 应该可以与 lambda 或 std::bind 一起使用。
  • 好的,明白了。我不明白如何解决这个问题。关于实例:在程序启动时,我将实例化一些类,这些变量将在程序结束之前可用。
  • @0x5453 你指的UB是当我使用bind而不是lambda的时候?
  • UB 正在读取超出范围的变量(成员函数指针)。要修复,您可以按值捕获指针,以便 lambda 将拥有它自己的指针副本,不会超出范围。对于instance,只要您在任何地方都保持不变,就可以了。可能也会留下一些cmets,这样你未来的自己就不会忘记。 (如果您确实希望 lambda 比对象更长寿,我可能会传递并按值捕获 std::shared_ptr&lt;Class&gt;,这样它就可以在不创建副本的情况下继续存在,因为 Class 可能会非常大。)

标签: c++ function c++11 lambda


【解决方案1】:

当对象或其副本超出当前范围时,不要使用[&amp;]

您正在捕获对局部变量的引用并将它们存储在当前范围之外。

【讨论】:

    【解决方案2】:

    正如评论中所说,您有function 的悬空指针,您可以改用:

    Caller(Class& instance, Return(Class::*function)(Args...))
    {
        callerFunction = [&instance, function](Args... args) {
            return (instance.*function)(std::forward<Args>(args)...);
        };
    }
    

    注意:instance 的寿命也应该比 Caller 长。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-18
      • 2023-04-05
      • 1970-01-01
      • 2012-05-22
      • 2019-09-17
      • 2016-05-17
      • 2021-12-27
      • 1970-01-01
      相关资源
      最近更新 更多