代码之家  ›  专栏  ›  技术社区  ›  Jeff

为什么[=]有lambda捕获?

  •  24
  • Jeff  · 技术社区  · 6 年前

    在直观的层次上,需要不带状态(通过引用或其他方式)的lambda应该可以清晰地转换为裸函数指针。但是,我最近很惊讶地看到GCC、CLang和MSVC中出现以下故障:

    int main(int, char *[]) {
        void (*fp)() = []{}; // OK
      //fp = [=]{};          // XXX - no user defined conversion operator available
      //fp = [&]{};          // XXX - same ...
    }
    

    C++ 17规范(或至少) visible public draft version N4713 )参见第8.4.5.1条第7项。 [表达式原始lambda.closure] 带或不带捕获物的羔羊:

    非泛型lambda表达式的闭包类型 没有lambda捕获 其满足约束(如果有的话)具有与C++语言链接(10.5)具有相同的参数和返回类型的函数指针的转换函数,该类型和返回类型作为闭包类型的函数调用操作符。…

    但是,从形式语法的角度来看,您可以在_§8.4.5中看到以下内容 [表达式原始lambda] :

    • lambda表达式 :
      • lambda引导器 复合语句
    • lambda引导器 :
      • [ λ俘获 选择 ]

    在第8.4.5.2节中 [表达式原始lambda.capture] :

    • λ俘获 :
      • 捕获默认值
      • 捕获列表
      • 捕获默认值,捕获列表
    • 捕获默认值 :
      • &
      • =

    所以所有的编纂者实际上都在遵守法律条文,令我沮丧…

    为什么语言将捕获的存在定义为声明中的狭义语法区别,而不是基于主体是否包含对任何非静态/捕获状态的引用?

    2 回复  |  直到 6 年前
        1
  •  11
  •   Shafik Yaghmour    6 年前

    国家机构的评论引发了允许这种转变的变化。参见 n3052: Converting Lambdas to Function Pointers 指国家机构 comment UK 42 :

    具有空捕获列表的lambda与常规函数类型具有相同的语义。通过要求这个映射,我们得到了一个高效的lambda类型,该类型具有已知的API,并且与现有的操作系统和C库函数兼容。

    以及来自 N3052 是:

    解决方法:添加新段落:“具有空捕获集的lambda表达式应可转换为指向函数类型r(p)的指针,其中r是返回类型,p是lambda表达式的参数类型列表。”此外,最好(a)允许转换为函数引用,以及(b)允许外部“c”函数指针类型。

    在第5段后添加新段落。此编辑的目的是为没有lambda捕获的lambda获取一个闭包到函数的指针转换。

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

    这就是我们今天的处境。注意所说的评论 空捕获列表 而我们今天所拥有的似乎与评论中所表达的意图相符。

    它看起来像是一个基于国家机构评论的修正案,应用范围很窄。

        2
  •  2
  •   T.C. Yksisarvinen    6 年前

    您提出的规则非常脆弱,尤其是在p0588r1之前的世界中,隐式捕获依赖于ODR的使用。

    考虑:

    void g(int);
    void h(const int&);
    
    int my_min(int, int);
    
    void f(int i) {
        const int x = 1, y = i;
        [=]{ g(x); }; // no capture, can convert?
        [=]{ g(y); }; // captures y
        [=]{ h(x); }; // captures x
        [=]{ my_min(x, 0); }; // no capture, can convert?
        [=]{ std::min(x, 0); }; // captures x
    }