【问题标题】:Is it safe to cast a lambda function to a function pointer?将 lambda 函数转换为函数指针是否安全?
【发布时间】:2013-03-17 10:12:44
【问题描述】:

我有这个代码:

void foo(void (*bar)()) {
    bar();
}

int main() {
    foo([] {
        int x = 2;
    });
}

但是,我担心这会遭受同样的命运:

struct X { int i; };

void foo(X* x) {
    x->i = 2;
}

int main() {
    foo(&X());
}

获取局部变量的地址。

第一个例子完全安全吗?

【问题讨论】:

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


    【解决方案1】:

    捕获 nothing 的 lambda 可以隐式转换为具有相同参数列表和返回类型的函数指针。只有无捕获的 lambda 才能做到这一点;如果它捕捉到任何东西,那么它们就不能。

    除非您使用的是 VS2010,它没有实现标准的那部分,因为在他们编写编译器时它还不存在。

    【讨论】:

      【解决方案2】:

      是的,我相信第一个示例是安全的,无论在评估涉及无捕获 lambda 表达式的完整表达式期间创建的所有临时对象的生命周期如何。

      根据工作草案 (n3485) 5.1.2 [expr.prim.lambda] p6

      没有 lambda 捕获的 lambda 表达式的闭包类型有 公共非虚拟非显式 const 转换函数到指针 函数具有与闭包相同的参数和返回类型 类型的函数调用运算符。此转换返回的值 function 应该是一个函数的地址,当它被调用时,有 与调用闭包类型的函数调用运算符的效果相同。

      上面的段落没有说明函数指针的有效性在 lambda 表达式评估后过期。

      例如,我希望以下工作:

      auto L = []() {
         return [](int x, int y) { return x + y; };
      };
      
      int foo( int (*sum)(int, int) ) { return sum(3, 4); }
      
      
      int main() {
        foo( L() );
      }
      

      虽然 clang 的实现细节肯定不是 C++ 的最终决定(标准是),但如果它让你感觉更好,那么在 clang 中实现的方式是在解析 lambda 表达式并在语义上分析一个闭包为 lambda 表达式发明了 -type,并在类中添加了一个静态函数,其语义类似于 lambda 的函数调用运算符。因此,即使 'L()' 返回的 lambda 对象的生命周期在 'foo' 的主体内已经结束,转换为指向函数的指针返回仍然有效的静态函数的地址。

      考虑一个类似的例子:

      struct B {
         static int f(int, int) { return 0; }
         typedef int (*fp_t)(int, int);
         operator fp_t() const { return &f; }
      };
      int main() {
        int (*fp)(int, int) = B{};
        fp(3, 4); // You would expect this to be ok.
      }
      

      我当然不是 core-c++ 专家,但是 FWIW,这是我对标准字母的解释,我觉得它是可以辩护的。

      希望这会有所帮助。

      【讨论】:

      • +1 表示“无论生命周期如何”。毕竟,创建的临时 lambda 对象被转换为函数指针,它的地址没有被使用。
      • 如果B::operator fp_t() 返回一个闭包,它还能正常工作吗?喜欢operator fp_t() const { return [](int x, int y) { return x + y; }; };
      • 有一个错字,但如果我错了,请纠正我:foo( L ) 有效,foo( L() ) 无效
      【解决方案3】:

      除了 Nicol 完全正确的一般性答案之外,我还要对您的特殊恐惧添加一些看法:

      但是,我担心这会遭受与...相同的命运, 获取局部变量的地址。

      当然可以,但是当您在 foo 中调用它时绝对没有问题(与您的 struct 示例完美工作的方式相同),因为定义的周围函数(在本例中为 main)无论如何,局部变量/lambda 将比被调用函数 (foo) 寿命更长。如果您可以保护该局部变量或 lambda 指针以供以后使用,这可能只是一个问题。所以

      第一个例子完全安全吗?

      是的,第二个例子也是。

      【讨论】:

      • "是的,第二个例子也是。" 学究式地说,第二个例子是非法的,因为它取了一个临时的地址,所以它是既不安全也不不安全。 ;-]
      • @ildjarn 哈,我这个白痴,我什至没有意识到这一点。那么现在该怎么办,从技术上讲,整个答案可能是垃圾,因为 lambda 示例也采用了临时地址。但话又说回来,它是一个 lambda,函数本身实际上应该一直“在那里”。也许是时候为这个看似很简单的问题制定标准了。
      • lambda 示例不采用临时地址,它只是将 lambda 对象转换为函数指针。无论如何,恕我直言,您的讽刺是错误的。
      • @enobayram “无论如何,恕我直言,您的讽刺是错误的” - 请您详细说明一下,您认为我的回答或评论的哪些部分是“讽刺” i> (不,这个评论并不是讽刺的,因为我没有任何讽刺的意思,我想知道哪一部分似乎是)。
      • 对不起,我想我读错了你的评论:),我以为你发现 ildjarn 的评论与主题无关,并且讽刺地说“从技术上讲,整个答案可能是垃圾"。
      猜你喜欢
      • 2012-01-27
      • 1970-01-01
      • 2018-06-15
      • 2018-10-29
      • 1970-01-01
      • 2015-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多