【问题标题】:Assigning pointer to lambda function to pointer to another lambda function将指向 lambda 函数的指针分配给指向另一个 lambda 函数的指针
【发布时间】:2015-11-25 08:09:52
【问题描述】:

我正在尝试将指向 lambda 函数的指针分配给指向另一个 lambda 函数的指针。代码会自己说话:

#include <iostream>

int main(int argc, char *argv[]) {

    auto l1 = []() { std::cout << "Lambda 1!" << std::endl; };
    auto l2 = [] { std::cout << "Lambda 2!" << std::endl; };

    auto l1p = &l1;
    l1p = &l2; // Why can't I do this assignment?
    return 0;
}

既然两个 lambda 函数的返回类型和参数相同,为什么我不能做这个赋值?

【问题讨论】:

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


    【解决方案1】:

    [expr.prim.lambda]/2:

    lambda-expression 的类型(也是 闭包对象)是一个唯一,未命名的非联合类类型——称为 闭包类型——其属性如下所述。

    也就是说,两种闭包类型总是完全不同且不相关。但是,在您的示例中,由于两个 lambda 都没有捕获任何内容,因此可以将它们转换为 void(*)() 类型的函数指针。
    所以也许你打算这样做

    void (*l1p)() = l1;
    l1p = l2;
    

    【讨论】:

    • 在 C++11(+) 中我更喜欢使用std::function&lt;void()&gt; l1f = l1;l1f = l2;,因为它更易于阅读。
    • @SimonKraemer 不要。 std::function 的开销不值得轻微的语法改进。只需使用typedef
    • @Quentin 我刚刚检查了编译器输出。我对巨大的开销感到非常惊讶。感谢您指出。
    • @SimonKraemer std::function 的名字真的不够吓人。它不是“一个函数”,它是一个类型擦除的函数容器。我听说过疯狂的编译器在某些情况下设法通过它进行优化,但你可能不想依赖它;)
    • @SimonKraemer 它是一个类型安全的包装器......对于 any 可调用。函数指针、带有 operator() 的手写类、lambda 等。这可能需要堆分配(以存储状态)(某些实现具有小对象优化),但确实涉及用于调度的 vtable 或等效项。调用开销类似于()处的虚函数调用,比函数指针调用略多,但更难内联。
    【解决方案2】:

    作为Columbo's answer 的补充,您可以使用operator + 进行类型推导和lambda-to-function-pointer 衰减:

    auto l1p = +l1; // l1p is void (*)()
    //         ^
    l1p = l2;
    

    【讨论】:

      【解决方案3】:

      让我们去掉 lambda 函数提供给我们的语法糖:

      struct lambda1 {
        void operator () {
          std::cout << "Lambda 1!" << std::endl;
        }
      };
      struct lambda2 {
        void operator () {
          std::cout << "Lambda 2!" << std::endl;
        }
      };
      int main(int argc, char *argv[]) {
      
          auto l1 = Lambda1{};
          auto l2 = Lambda2{};
      
          auto l1p = &l1; // l1p is a Lambda1 *
          l1p = &l2; // &l2 is a Lambda2 *
          return 0;
      }
      

      这不是编译器完成的确切转换,但我认为检查问题就足够了:

      这两个 lambda 都有自己的类型,这与它们的参数、捕获的变量或返回类型完全无关。当然,lambdas 的类型彼此不相关,因此您显然不能从另一个地址分配指向其中一个的指针。


      另一个答案中带有函数指针的解决方案非常好。如果 C++ 没有“只有在使用时才付费”的原则,我们可以制作像这样的 lambda 函数

      struct SomeLambda
        : public LambdaBase<
              Capture<int>,
              Arguments<bool>,
              Return<void>> {
        // ...
      };
      /*
        [integer]
        (bool flag) {
          if (flag) cout << integer;
        }
      */
      

      但要做到这一点,基需要是多态的(具有虚拟成员函数),如果你不打算做你想做的事,那是你不需要的成本。

      【讨论】:

      • 我明白了这个例子的重点。但抛开问题不谈,C++ lambda 真的是 (C++1) 闭包的语法糖吗?
      • 据我所知,是的 (stackoverflow.com/q/7627098/1116364) 他们只是糖。
      猜你喜欢
      • 1970-01-01
      • 2016-10-09
      • 1970-01-01
      • 2013-01-16
      • 2016-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-16
      相关资源
      最近更新 更多