【问题标题】:Lambda expressions as class template parameters in C++14Lambda 表达式作为 C++14 中的类模板参数
【发布时间】:2014-10-02 19:27:49
【问题描述】:

问题Lambda expressions as class template parameters 询问是否可以使用 lambda 表达式作为类模板参数。

这个问题的答案是否定的。但是,它是关于 C++11 的。

新标准 C++14 的情况是否发生了变化?

【问题讨论】:

  • 规则在 C++14 中收紧了,考虑到rationale for excluding them form unevaluated operands,我认为这不会改变。
  • 鉴于最后的草稿,链接帖子中引用的文本(带强调)没有改变。所以我想,不,情况没有改变。
  • 呃...答案是肯定的,只是不是直接的。呈现我的答案,这对于 C++14 也是“是的,只是不直接”,有点毫无意义。

标签: c++ templates lambda language-lawyer c++14


【解决方案1】:

不,C++14 中的情况根本没有改变,事实上,5.1.2Lambda 表达式第 2 部分中的语言已经从以下方面收紧:

lambda 表达式不得出现在未计算的操作数中(子句 5).

到:

[...]lambda 表达式不应出现在未计算的操作数中 (第 5 条),在模板参数中,在别名声明中,在 typedef 声明,或在函数或函数的声明中 函数体和默认参数之外的模板。 [注: 目的是防止 lambda 出现在签名中。 -结尾 注意][...]

Defect report 1607. Lambdas in template parameters 导致了这种变化。

缺陷报告只是间接地处理了不允许这样做的理由,但我们可以在Rationale for lambda-expressions not being allowed in unevaluated contexts 中找到关于为什么不允许这样做的非常详细的解释。原因归结为:

  • Lambda 表达式没有唯一的类型
  • 编译器实现问题:
    • 如SFINAE的非凡扩展
    • 命名 mangle 整个 lambda 主体的可能要求。

鉴于此限制的基本原理,它似乎不太可能改变。

【讨论】:

    【解决方案2】:

    新标准 C++14 的情况是否发生了变化?

    Lambda 表达式仍不应出现在未计算的操作数中 - 与 Xeo's Post 中的引用相同的引用也存在于最新的公开可用草案 N3797 中,位于完全相同的位置。

    但是,每个闭包类型都有一个已删除的默认构造函数(N3797,§5.1.2/20):

    与 lambda-expression 关联的闭包类型已删除 (8.4.3) 默认构造函数和一个已删除的复制赋值运算符。

    因此,为了可移植性和标准一致性(并且可能为了代码在合理的编译器上工作),您需要将闭包对象传递给实例化类的构造函数以进行复制。但是要传递一个与该专业化的模板参数类型相同的闭包对象,无论如何您都必须先定义它:

    using my_map_type = map<int, int, decltype([] (auto&& lhs, auto&& rhs) {return lhs < rhs*4;})>;
    // Assuming the above compiles
    
    my_map_type m( [] (auto&& lhs, auto&& rhs) {return lhs < rhs*4;} );
    // Different closure type - compiler error! What do you copy from!? 
    

    没有任何合法的方法可以创建第一个 lambda 闭包类型的单个对象。因此,即使要删除该规则,您也无法创建my_map_type 的单个实例。其他“作为模板参数的闭包类型”场景也会出现类似的问题。

    【讨论】:

    • “Lambda 表达式仍不应出现在未计算的操作数中” 但它可以出现在常量表达式的未计算部分中。如果您没有闭包对象,这甚至允许在不捕获的情况下使用 lambda,请参阅pfultz2.github.io/Pythy
    • 9.3.1 非静态成员函数 [class.mfct.non-static] p2:“如果为非 X 类型的对象调用类 X 的非静态成员函数,或从 X 派生的类型,行为未定义。” (我想你可能会争辩说这个措辞是有缺陷的,因为通过空指针调用意味着函数不会在任何对象上调用,因此不会在类型不同于 X 的对象上调用,但人们普遍认为,非-static 成员函数需要一个实例。)
    • @ShafikYaghmour 支持的扩展和编译器根本无法检测到未检测到的行为的程序有所不同。 __builtin_constant_p 的这种特殊用法是受支持扩展的一个很好的例子,如果您不介意您的代码只能与 GCC 和 clang 一起使用,请务必使用它。对于这里的“理查德史密斯建议”,我不清楚这是否意味着他推荐它,或者只是他提出了这个想法。如果他推荐它,那么我不反对任何人使用该技巧与clang。 :)
    • @dyp 还有issue 315,其中明确声明通过取消引用的空指针调用静态成员函数是明确定义的。我们可以由此得出结论,仅取消引用空指针的行为是有效的(即使将它绑定到引用可能不是)。
    • 正如我所说,我不清楚取消引用空指针是否实际上是 UB。我将其称为(*static_cast&lt;closure_type_ptr&gt;(nullptr)) () 或closure_type_ptr()(),但我不确定我是否会使用它,因为它至少在标准和compiler support 的灰色区域中。
    猜你喜欢
    • 1970-01-01
    • 2016-08-21
    • 2011-04-18
    • 1970-01-01
    • 2023-03-08
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多