【问题标题】:A positive lambda: '+[]{}' - What sorcery is this? [duplicate]一个积极的 lambda: '+[]{}' - 这是什么魔法? [复制]
【发布时间】:2013-09-24 04:54:11
【问题描述】:

在堆栈溢出问题Redefining lambdas not allowed in C++11, why?中,给出了一个无法编译的小程序:

int main() {
    auto test = []{};
    test = []{};
}

问题已得到解答,一切似乎都很好。然后是Johannes Schauban interesting observation

如果您在第一个 lambda 之前添加 +,它就会神奇地开始工作。

所以我很好奇:为什么以下工作有效?

int main() {
    auto test = +[]{}; // Note the unary operator + before the lambda
    test = []{};
}

它与GCC 4.7+ 和Clang 3.2+ 都可以正常编译。代码标准符合吗?

【问题讨论】:

  • 有趣的是,对于捕获 lambda,它不起作用。
  • @MatthieuM。因为捕获 lambdas 不会衰减为函数指针! ;)
  • 另一个+ sourcery 紧随其后。在 GCC 上试试这个:struct foo { static const int n = 100; }; int main() { return std::max(0, +foo::n); }。如果您删除 + 它无法链接,这是符合标准的行为。 VS2010 连接起来没有问题(即使没有+)。
  • 让我们添加更多魔法:auto test = *[]{};(注意x 在这里仍然是一个函数指针,我认为由于衰减)然后..auto test = +*[]{};。当然,您可以无限重复此操作:auto test = *+*+*+[]{};。还有我最喜欢的:auto test = +*??(:>()<%??>;

标签: c++ c++11 lambda operator-overloading language-lawyer


【解决方案1】:

是的,代码符合标准。 + 触发转换为 lambda 的普通旧函数指针。

会发生什么:

编译器看到第一个 lambda ([]{}) 并根据 §5.1.2 生成一个闭包对象。由于 lambda 是 非捕获 lambda,因此适用以下情况:

5.1.2 Lambda 表达式 [expr.prim.lambda]

6 没有 lambda-capturelambda-expression 的闭包类型具有公共的非虚非显式 const 转换函数指向具有与闭包类型的函数调用运算符相同的参数和返回类型的函数的指针。这个转换函数的返回值应该是一个函数的地址,当被调用时,它与调用闭包类型的函数调用运算符具有相同的效果。

这很重要,因为一元运算符 + 有一组内置的重载,特别是这个:

13.6 内置运算符 [over.built]

8 对于T 的每种类型,都存在以下形式的候选运算符函数

    T* operator+(T*);

有了这个,很清楚会发生什么:当运算符+ 应用于闭包对象时,重载的内置候选集包含一个转换为任意指针,而闭包类型恰好包含一个候选: 转换为 lambda 的函数指针。

auto test = +[]{}; 中的test 的类型因此推导出为void(*)()。现在第二行很简单:对于第二个 lambda/closure 对象,对函数指针的赋值会触发与第一行相同的转换。即使第二个 lambda 具有不同的闭包类型,生成的函数指针当然是兼容的并且可以分配。

【讨论】:

  • 令人着迷。指针的一元 + 有什么意义?我知道它存在于数字类型中,以确保一元 - 的完整性。但是带有指针的一元 - 没有意义。
  • 如果你想强制数组或函数衰减,如果你想强制提升小整数类型或无范围枚举,这很有用。
  • @TadeuszKopec ...对于所有类型,它都删除了左值性。您可以使用它来传递值而不是对两者都重载的函数的引用,例如通过完美转发。
  • @Potatoswatter: “对于所有类型,它都会删除左值性。” +lvalue 不是为所有类型定义的,对于其中的一些类型,它是只是一个重载的函数调用,因此可以产生任何不相关类型的任何值类别,但对于某些其他情况,它不会产生相同的类型。使用它来传递值而不是引用将是一个奇怪的用途。更好的方法是使用static_cast<T>(lvalue) 从左值获取 T 类型的纯右值。 “习惯使用一个叫做 val() 的通用函数”:这个不清楚,你能给出这样一个val() 函数的例子/链接吗?
  • @user1131467 我的意思是,如果你不是专门使用内置的operator+,你应该定义一个函数val,它可以有效地执行你提到的static_cast,而不是坚持前缀+ 符号。
猜你喜欢
  • 2013-02-04
  • 2012-05-14
  • 2016-05-15
  • 1970-01-01
  • 2010-10-30
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
相关资源
最近更新 更多