gcc 4.9递归lambda返回时的内部编译器错误

时间:2014-02-02 16:08:56

标签: c++ gcc c++14 gcc4.9

我有一小段代码可以在clang repo head(3.5)中编译好但在gcc 4.9 repo head中没有。虽然这看起来像一个gcc bug,但在发送bugzilla之前我想问你是否

  1. 这是有效的c ++ 1y代码(在当前的草稿状态中) - 只是因为clang编译它没有理由让它成为正确的代码,并且
  2. 如果有人可以重现这个错误。
  3. 使用clang编译和运行的代码片段在这里:

    http://coliru.stacked-crooked.com/a/acc691b9a407d6f2

    但是使用

    g++-4.9 -o main main.cpp -std=c++1y
    

    给出了上述内部编译器错误:http://pastebin.com/3fqV7xzC

    如果没有长转储,则会显示:

      

    g ++ - 4.9 -o main main.cpp -std = c ++ 1y       main.cpp:在'composer :: operator()的实例化中(Func&&,Funcs&& ...):: [with auto:2 = float; Func = main(int,const char * )::; Funcs = {main(int,const char *)::}]':       main.cpp:33:88:从这里要求

    main.cpp:19:41: internal compiler error: in retrieve_specialization, at cp/pt.c:1042
        return f(c(std::forward<Funcs>(fs)...)(v));
                                             ^
    

    为了完整起见,这里是片段(完整的main.cpp)

    #include <iostream>
    #include <utility>
    
    template <typename... Funcs>
    struct composer;
    
    template <>
    struct composer<> {
        auto operator()() {
            return [&] (auto v) { return v; };
        }
    };
    
    template <typename Func, typename... Funcs>
    struct composer<Func, Funcs...> {
        auto operator()(Func&& f, Funcs&&... fs) {
            composer<Funcs...> c;
            return [&] (auto v) {
                return f(c(std::forward<Funcs>(fs)...)(v));
            };
        }
    };
    
    template <typename... Funcs>
    auto compose(Funcs&&... fs) {
        composer<Funcs...> c;
        return c(std::forward<Funcs>(fs)...);
    }
    
    
    int main (int argc, char const* argv[]) {
        float v = 3.5f;
        auto t = compose([] (auto v) { return v >= 3; }, [] (auto v) { return int(v-0.5); })(v);
        std::cout << std::boolalpha << t << "\n";
        auto f = compose([] (auto v) { return v > 3; }, [] (auto v) { return int(v-0.5); })(v);
        std::cout << std::boolalpha << f << "\n";
    }
    

    修改:加分!我根本不喜欢那些代码 - 如果有人有更好的,可能更快的方法来考虑启发我......

    编辑2 有谁知道怎么让coliru(或类似服务)使用g ++ 4.9?

1 个答案:

答案 0 :(得分:4)

您的代码无效C ++ 1y,至少在执行时无效。

通过引用捕获变量,然后退出定义它们的范围,然后调用使用所述变量的lambda。

现在,c的状态从未使用过,但operator()调用仍然是UB。

同样,虽然您的引用是与当前范围相比的数据,但无法保证捕获原始变量而不是本地引用。实现本地捕获的一种方法是捕获指向本地堆栈帧的指针,并通过来自所述堆栈帧的编译时静态偏移来访问变量:当您退出堆栈帧时,这样的读取将产生垃圾。 (这会将[&] lambda的大小减小到单个指针,这是一个非常好的优化!在某些机器上,通过指针的偏移访问数据的速度很快。)

一般情况下,如果您的闭包(或其副本)将比当前范围更长,则不要通过引用(特别是通过全局引用)进行捕获。对return语句的全局引用捕获应该会产生警告。

修复UB和您的代码看起来有效。在C ++ 1y中,您可以捕获 - move或 - forward

如果没有C ++ 1y编译器,可以尝试一下:

#include <iostream>
auto compose() {
  return [](auto&& x) { return std::forward<decltype(x)>(x); };
}

template<typename F>
auto compose( F&& f ) {
  return std::forward<F>(f);
}

template<typename F1, typename F2>
auto compose( F1&& f1, F2&& f2 ) {
  return [lhs = std::forward<F1>(f1), rhs = std::forward<F2>(f2)]( auto&& x ) {
    return lhs( rhs( std::forward<decltype(x)>(x) ) );
  };
}

template<typename F0, typename... Fs>
auto compose( F0&& f0, Fs&&... fs ) {
  static_assert( sizeof...(Fs) > 1, "other overrides should have handled this case!  What went wrong?" );
  return compose(
    std::forward<F0>(f0),
    compose(
      std::forward<Fs>(fs)...
    )
  );
}

int main() {
  auto a = compose( [](bool b){return !b;}, [](double d){ return d<3.0; } );
  std::cout << a(2.0) << "," << a(3.0) << "\n";
  return 0;
}

也恰好也是非lambda类型。