标准保证了lambda的可继承性吗?

16
在C++标准中,闭包类型定义如下:

[expr.prim.lambda.closure] lambda表达式(也是闭包对象的类型)的类型是一种独特的、未命名的非联合类类型,称为闭包类型,其属性如下所述。[...]

标准似乎没有定义未命名非联合类类型是否是final的问题。如果一个实现将lambda实现为final类,那么它是否符合标准?或者说,我们有保证可以从lambda继承吗?
这个问题不在于是否有用或者没有用从lambda继承:已经假设它是有用的了。问题在于标准是否提供了这个保证。

2
@Matteo 可以看一下 std::visit,在我的观点中,将 lambda 函数串联起来(甚至与其他函数对象一起)比使用 if 语句链的通用 lambda 函数更加简洁。还可以参考 P0051 - Rakete1111
6
一个未命名的类不能被声明为 final。 :) - cpplearner
2
@Rakete1111 这确实有意义,因为final的效果与语法形式相关联。 :) - cpplearner
2
@Vincent,这是语法不允许有一个未命名的最终类,因为final不是关键字。class final是一个名为final的类。 - Rakete1111
1
@cpplearner 嗯,非常有趣。我想你的解释是可行的。不错 :) 谢谢 - Rakete1111
显示剩余4条评论
2个回答

18

是的,闭包类型必须不是 final。至少这是我的理解。

§8.1.5.1 闭包类型 [expr.prim.lambda.closure]

一个实现可以从下面所描述的方式不同地定义闭包类型,只要这样做不改变程序的可观察行为,除非通过更改:

  • ... [不适用]

然后标准并没有将闭包类型描述为 final。使它成为 final 将改变可观察行为,因此闭包类型必须不是 final。

关于可观察行为。考虑以下情况:

auto l = []{};
return std::is_final_v<decltype(l)>;

将闭包类型设为final显然会修改有效程序的可观察行为。


至于用例,它实际上可以是一个非常有用的功能:

template <class... Fs> struct Overload : Fs ...
{
    using Fs::operator()...;
};

template <class... Fs> Overload(Fs...) -> Overload<Fs...>;

auto test()
{
    Overload f = {[] (int a) { return a * 100; },
                  [] (int a, int b) { return a + b;}};

    return f(1) + f(2, 3); // 105
}

godbolt上看它的实际应用。


感谢hvd和rakete1111在评论中的讨论和反馈。


1
逻辑应该反过来:如果标准不禁止实现使生成的类型为final,则实现可以自由地这样做。幸运的是,标准似乎禁止实现这样做:[expr.prim.lambda]p3或[expr.prim.lambda.closure]p2。但我并不完全确定。 - user743382
2
@hvd,您能引用标准禁止闭包类型为final的地方吗? - Vincent
2
@Vincent,“[...] provided this does not alter the observable behavior of the program other than by changing [...]”这一部分:使闭包类型变为final将改变可观察行为。我不是完全确定其中未指定的方面和接下来的段落是否也被包含在内。 - user743382
1
@hvd 1) [intro.abstract]p6 不同意你的观点。2) 是的,但这也存在与可继承性相同的问题,因为它说“[...]与下面描述的不同[...]”,所以无法应用。3) 哎呀,我总是假设最新的草案,抱歉。 - Rakete1111
1
@hvd 哦,我明白了。那种解释也是合理的,抱歉。我的意思是,没有任何描述最终状态的内容,因此无法确定实现是否有所不同。 - Rakete1111
显示剩余4条评论

0

final 的效果在 [class]/3 中有所说明:

如果一个类被标记为 class-virt-specifier final 并且它出现在一个 base-clause 中的 class-or-decltype 中,那么程序就是不合法的。

也就是说,这并不关心这个类是否是 final。重要的是这个类是否被标记为 final 限定符。由于闭包类型在源文件中没有任何声明,因此无法将其标记为 final,因此 [class]/3 不适用。


7
这是一个完全荒谬的解释。按照相同的逻辑,Lambda 表达式也不可调用,因为“函数声明使用以下其中之一的运算符函数标识符作为其名称声明了一个运算符函数。”不适用于由 Lambda 提供的 operator(),因为它不是通过 运算符函数标识符 语法结构引入的。 - user743382
1
“Operator function”是斜体字。根据上下文不同,斜体字可能有不同的含义,但在这个上下文中,它表示这是术语的定义(见[intro.defs]p3),因此除了由[见引用]声明的函数外,不存在所谓的运算符函数。 - user743382
1
从我的英语知识来看,这种语言不起作用。 - cpplearner
3
你读过[intro.defs]p3了吗?在普通英语中斜体是怎样的并不重要,重要的是在C++标准中斜体是如何工作的。 - user743382
1
@PasserBy 说得有道理,但是... https://github.com/cplusplus/draft/issues/1820#issuecomment-345398277 - cpplearner
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接