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

Posted

技术标签:

【中文标题】递归 lambda 返回时的 gcc 4.9 内部编译器错误【英文标题】:gcc 4.9 internal compiler error on recursive lambda return 【发布时间】:2014-02-02 16:08:55 【问题描述】:

我有一小段代码可以在 clang repo head (3.5) 中编译,但在 gcc 4.9 repo head 中编译得不好。虽然这看起来像一个 gcc 错误,但在向 bugzilla 发送垃圾邮件之前,我想问你是否

    这是有效的 c++1y 代码(处于当前草稿状态) - 仅仅因为 clang 编译它,它没有理由成为正确的代码,并且 如果有人可以重现此错误。

使用clang编译运行sn-p的代码在这里:

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));
                                         ^

为了完整起见,这里是 sn-p(完整的 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?

【问题讨论】:

if anyone can reproduce this bug.你检查过bug tracker吗? 很遗憾,我没能找到与这件事相关的错误,没有。 我可以在gcc version 4.9.0 20131223 (experimental) (GCC) 上重现这个ICE 好的,这是一个问题,谢谢。不管这段代码是否正确,它都不应该产生 ICE,所以我会把它发布到跟踪器...... 一个内部编译器错误表明这肯定是一个 gcc 错误;无论代码本身是有效还是无效,这都不应该发生。您可能希望在问题中至少包含错误消息的前几行。 【参考方案1】:

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

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

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

同样,虽然您的引用是对超出当前范围的数据的引用,但不能保证捕获的是原始变量而不是本地引用。实现本地捕获的一种方法是捕获指向本地堆栈帧的指针,并通过所述堆栈帧的编译时静态偏移量访问变量:当您退出堆栈帧时,这样的读取会产生垃圾。 (这会将[&amp;] 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 类型的。

【讨论】:

嗯......你可能有一个有效的观点,在这里。关于您在示例中的转发捕获,您可能想看看我的问题“通过通用参考捕获”,尤其是答案:***.com/questions/21238463/…(第二个解释了为什么[x = std::forward&lt;T&gt;(x)] 可能没有达到您的预期到) @RichardVock 嗯,它会复制。如果在所有情况下都效率不高,那至少是安全的。根据传入的值是右值还是左值,不确定如何通过引用或复制有条件地捕获:可能是按值捕获的条件引用包装器或 T 实例?注意我确实说过我缺少 C++1y 编译器,所以上面的代码有点盲目。 :) 这很好。一旦 ICE 被弄清楚,我会更深入地研究这个问题。 Adam 目前正在调查此事(这似乎比我想象的要复杂):gcc.gnu.org/bugzilla/show_bug.cgi?id=60033 哦,顺便说一句:如果你使用 coliru,然后使用 clang++ -std=c++1y,你会得到一个已经存在的 c++1y 编译器符合标准...coliru.stacked-crooked.com

以上是关于递归 lambda 返回时的 gcc 4.9 内部编译器错误的主要内容,如果未能解决你的问题,请参考以下文章

使用 lambda 而不是显式匿名内部类时的不同泛型行为

Lambda演算 - 简述Y组合子的作用

编译安装 gcc 4.9并验证使用

如何设置gcc5.0与gcc4.9的优先级

即使在安装 4.9 (Homebrew) 后 gcc 版本仍显示 4.2.1

mplayer-ww-37356 rebuild by gcc 4.9 and gcc 5.1