C++0x 闭包的未定义行为:I

发布于 2024-10-30 14:42:12 字数 859 浏览 1 评论 0原文

考虑这个例子:

#include <iostream>
#include <functional>     // std::function
#include <vector>        // std::vector
#include <algorithm>    // std::for_each

int main(){

    auto adder = [](int x) {
        return [&](int y) { 
            return x+=y; 
        }; 
    };

    std::vector < std::function<int(int)> > vec;

    vec.push_back(adder(1));
    vec.push_back(adder(10));

    std::for_each(vec.begin(), vec.end(), [](std::function<int(int)> f){std::cout << f(33) << " ";});
    std::cout << std::endl;
}

我们期望整数 34 和 43 4376,但 gcc 4.6.0 却生成“内部编译器错误:分段错误” ”。代码有什么问题吗?

编辑:此处讨论了其他几个示例。

Consider the example:

#include <iostream>
#include <functional>     // std::function
#include <vector>        // std::vector
#include <algorithm>    // std::for_each

int main(){

    auto adder = [](int x) {
        return [&](int y) { 
            return x+=y; 
        }; 
    };

    std::vector < std::function<int(int)> > vec;

    vec.push_back(adder(1));
    vec.push_back(adder(10));

    std::for_each(vec.begin(), vec.end(), [](std::function<int(int)> f){std::cout << f(33) << " ";});
    std::cout << std::endl;
}

One expects the integers 34 and 43 43 and 76, but instead gcc 4.6.0 produces "internal compiler error: Segmentation fault". What is wrong with the code?

Edit: Several other examples are discussed here.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦晓ヶ微光ヅ倾城 2024-11-06 14:42:12

(编辑:这当然不能解释 ICE;我太仓促地阅读了原始问题。)

代码中的一个问题是从 adder 函数返回的 lambda 表达式包含对不再存在的 x 变量的悬空引用。通过副本([=][i])而不是引用([&])捕获,一切都应该正常。

(Edit: this certainly does not explain the ICE; I read the original question too hastily.)

The One problem in that code is that the lambdas returned from the adder function contain dangling references to the x variable that no longer exists. Capture by copy ([=] or [i]) instead of a reference ([&]) and everything should work.

灯下孤影 2024-11-06 14:42:12

看来,在您的示例中,无法省略 Trailing-return-type 。以下是标准(5.1.2 Lambda 表达式)的摘录:

如果 lambda 表达式不
包括尾随返回类型,它是
就好像尾随返回类型表示
以下类型: — 如果复合语句的形式为 { attribute-specifier-seq return expression ;左值到右值转换 (4.1)、数组到指针转换 (4.2) 和函数到指针转换 (4.3) 后返回表达式的类型; ——否则无效。

您示例中的返回值不能用于上述转换。以下显式添加返回类型的代码可在 VS 2010 中编译:

auto adder = [] (int x) -> std::function<int (int)> {
  return [=]( int y ) {
    return x + y;
  };
};

It seems, that in your example trailing-return-type cannot be omitted. Here is excerpt from standard (5.1.2 Lambda expressions):

If a lambda-expression does not
include a trailing-return-type, it is
as if the trailing-return-type denotes
the following type: — if the compound-statement is of the form { attribute-specifier-seq return expression ; } the type of the returned expression after lvalue-to-rvalue conversion (4.1), array-to-pointer conversion (4.2), and function-to-pointer conversion (4.3); — otherwise, void.

Returned value in your example cannot be used for conversions mentioned above. Following code with explicitely added return type compiles in VS 2010:

auto adder = [] (int x) -> std::function<int (int)> {
  return [=]( int y ) {
    return x + y;
  };
};
妳是的陽光 2024-11-06 14:42:12

你完全没有抓住要点。对 std::function 的需求非常非常明显。

  1. 所有 lambda 在编译时都有唯一的类型
  2. 您希望向量在运行时保存任何函数对象。
  3. 因此,需要某种类型擦除,这就是 std::function 所做的工作。

你究竟如何创建一个在运行时与编译时事实不同的向量,就像其中包含的类型一样?这在逻辑上是不可能的——除非您使用诸如 std::function 之类的抽象。

当然,如果您只想要其中一种 lambda 类型,那么您根本不需要 std::function 。不过这种情况相对较少。

int main() {
    auto adder = [](int x) {
        return [=](int y) {
            return x + y;
        };
    };
    // alternatively- you MUST copy the argument as it will cease to exist
    // but once it's in the lambda, you can use "mutable" to allow you to
    // modify the copy that each lambda has.
    /*
    auto adder = [](int x) {
        return [=](int y) mutable {
            return x += y;
        };
    };
    */
    std::vector<decltype(adder(0))> adders;
    adders.emplace_back(adder(0));
    adders.emplace_back(adder(1));

    std::for_each(adders.begin(), adders.end(), [](decltype(*adders.begin())& ref) {
        std::cout << ref(33);
    });
    std::cin.get();
}

MSVC 实际上不会编译这个小片段,但我认为这是一个错误,并且根据编译器的报告判断,我希望它能够在那里编译并且确实可以正常工作。

You're just completely missing the point. The need for std::function is very, very obvious.

  1. All lambdas have a unique type at compile-time
  2. You want the vector to hold any functional object at run-time.
  3. Therefore, some sort of type erasure is required, which is the job std::function does.

How on earth could you ever create a vector that varies at run-time a compile-time fact, like the type contained within it? That's just logically impossible- unless you use an abstraction such as std::function.

Of course, if you only ever want one lambda type within, then you don't need std::function at all. This is relatively rare though.

int main() {
    auto adder = [](int x) {
        return [=](int y) {
            return x + y;
        };
    };
    // alternatively- you MUST copy the argument as it will cease to exist
    // but once it's in the lambda, you can use "mutable" to allow you to
    // modify the copy that each lambda has.
    /*
    auto adder = [](int x) {
        return [=](int y) mutable {
            return x += y;
        };
    };
    */
    std::vector<decltype(adder(0))> adders;
    adders.emplace_back(adder(0));
    adders.emplace_back(adder(1));

    std::for_each(adders.begin(), adders.end(), [](decltype(*adders.begin())& ref) {
        std::cout << ref(33);
    });
    std::cin.get();
}

MSVC won't actually compile this little snippet, but I think that's a bug and judging by the reports of your compiler, I expect that it will compile there and indeed work correctly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文