使用C ++ 0x Closure的未定义行为:I

时间:2011-04-04 19:13:57

标签: lambda c++11 closures pass-by-reference

考虑一下这个例子:

#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 43 76 ,但gcc 4.6.0会产生“内部编译器错误:分段错误” 。代码有什么问题?

编辑:讨论了其他几个例子here

3 个答案:

答案 0 :(得分:2)

(编辑:这当然不能解释ICE;我太匆忙地读了原来的问题。)

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

答案 1 :(得分:0)

看来,在你的例子中,trailing-return-type不能省略。以下是标准(5.1.2 Lambda表达式)的摘录:

  

如果lambda表达式没有   包括尾随返回类型,它是   好像尾随返回类型表示   以下类型: - 如果compound-statement的格式为{attribute-specifier-seq return expression; lvalue-to-rvalue转换(4.1),数组到指针转换(4.2)和函数到指针转换(4.3)之后返回表达式的类型; - 否则,无效。

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

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

答案 2 :(得分:0)

你只是完全忽略了这一点。对std::function的需求非常非常明显。

  1. 所有lambdas在编译时都有一个唯一的类型
  2. 您希望向量在运行时保存任何功能对象。
  3. 因此,需要某种类型的擦除,这是作业std::function所做的。
  4. 你到底怎么能创建一个在运行时变化的编译时事实,就像它包含的类型一样?这在逻辑上是不可能的 - 除非你使用诸如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实际上不会编译这个小片段,但我认为这是一个错误并且根据编译器的报告来判断,我希望它会在那里编译并确实正常工作。