【问题标题】:Segmentation fault when calling lambda returns from high-order function in C++11调用 lambda 从 C++11 中的高阶函数返回时出现分段错误
【发布时间】:2018-04-07 10:58:43
【问题描述】:

我有以下代码试图以 CPS 形式定义程序。

#include <iostream>
#include <utility>
#include <type_traits>
#include <string>

using namespace std;


template <typename T> using Func = std::function<T>;
template <typename T> using Cont = Func<void (T &&)>;
template <typename T, typename U> using Proc = Func<void (T &&, Cont<U>)>;

template <typename T> struct RevProc;
template <typename T, typename U> struct RevProc<Proc<T, U>> {
  using ArgType = T;
  using RetType = U;
};

template <typename P1, typename P2> auto pipe(P1 proc1, P2 proc2)
  -> Proc<typename RevProc<P1>::ArgType, typename RevProc<P2>::RetType> {
  using T = typename RevProc<P1>::ArgType;
  using U = typename RevProc<P2>::ArgType;
  static_assert(is_same<U, typename RevProc<P1>::RetType>::value,
    "Mismatch procedure type.");
  using V = typename RevProc<P2>::RetType;
  return [&] (T &&t, Cont<V> pass) {
    proc1(move(t), [&] (U &&u) {
      proc2(move(u), pass);
    });
  };
}
template <typename P1, typename P2, typename... Ps> auto pipe(P1 proc1, P2 proc2, Ps ...procs)
  -> Proc<typename RevProc<P1>::ArgType, typename RevProc<decltype(pipe(proc2, procs...))>::RetType> {
  auto proc2s = pipe(proc2, procs...);
  return pipe(proc1, proc2s);
}

int main() {
  Func<void ()> var1;
  Cont<int> var2([] (int &&x) {});
  Proc<int, int> var3([] (int &&x, Cont<int> pass) {
    pass(x + 1);
  });
  auto var4 = pipe(var3, var3);
  var4(42, [] (int &&x) {
    cout << x << endl;
  });
  Proc<string, int> var5([] (string &&str, Cont<int> pass) {
    pass(str.length());
  });
  // auto var6 = pipe(var5, var5);
  Proc<int, int> var7 = pipe(var3, var3, var3);
  // var7(42, [] (int &&x) {
  //   cout << x << endl;
  // });
  auto var8 = pipe(var5, var3, var3);
  var8("something", [] (int &&x) {
    cout << x << endl;
  });

  return 0;
}

如果我取消注释 var6 行,编译器会按预期抛出错误。但是,如果 var7var8 的调用未注释,则代码通过编译但会在运行时触发随机分段错误或总线错误。代码在构建 lambda 时是安全的,但在应用时会崩溃。

感谢您指出我的错误。


如果我将代码修改为,补充它运行良好可能会很有用

var7 = pipe(var3, pipe(var3, var3));

var8


我随机尝试并修复了,令我惊讶的是。我只保留第一个声明并通过复制第一个来修改第二个:

template <typename P1, typename P2, typename... Ps> constexpr auto pipe(P1 proc1, P2 proc2, Ps ...procs)
  -> Proc<typename RevProc<P1>::ArgType, typename RevProc<decltype(pipe(proc2, procs...))>::RetType> {
  using T = typename RevProc<P1>::ArgType;
  using U = typename RevProc<P2>::ArgType;
  static_assert(is_same<U, typename RevProc<P1>::RetType>::value,
    "Mismatch procedure type.");
  using V = typename RevProc<P2>::RetType;
  return [&] (T &&t, Cont<V> pass) {
    proc1(move(t), [&] (U &&u) {
      pipe(proc2, procs...)(move(u), pass);
    });
  };
}

那么,到底是什么?

【问题讨论】:

  • 声明auto proc2s却不使用是什么原因?
  • 对不起,那只是调试人员。已编辑。

标签: c++ c++11 lambda functional-programming segmentation-fault


【解决方案1】:

您的管道实现具有未定义的行为。它通过引用捕获所有内容,管道的函数参数也是如此,这些参数在管道函数结束后被销毁。通过复制 [=] 捕获,或通过引用传递给管道。

【讨论】:

  • 太棒了。事实证明,只有 proc1 可以通过引用捕获,因为调用 proc2pass 的 lambda 的构造被延迟。很难从具有 GC 的语言中转变我的想法。非常感谢。
  • 顺便说一句,经过努力,我想出了如何编写pipe的CPS版本,它能够捕获所有lambda级别的所有内容作为引用。很有趣!
猜你喜欢
  • 1970-01-01
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
相关资源
最近更新 更多