为什么访问我的capture-by-reference变量会导致我的lambda函数出现段错误?

时间:2017-08-18 23:39:43

标签: c++ c++11 lambda

我将介绍本地C / C ++ Meetup的lambda表达式,所以我准备了几个示例来演示lambda表达式如何解决某些任务。

其中一个示例是创建一个qsort函数,该函数为比较函数提供lambda表达式。它适用于下面sort_ascending()的简单情况,但对于sort_descending(),其中我包含一个通过引用捕获的计数器整数,并且每次调用lambda表达式都会递增,我得到一个段错误首先尝试增加计数器。

我查看了我能找到的lambda表达文档,尤其是cppreference.cpp。据我所知,testcount中的sort_descending没有理由超出范围:testcountqsort_l的调用位于同一堆栈框架中{1}},testcount返回时,不再使用捕获qsort_l的lambda表达式。

// -*- compile-command: "g++ -std=c++11 -ggdb -Wno-pmf-conversions -Wall -Werror -Weffc++ -pedantic -o minfail minfail.cpp" -*-

#include <stdlib.h>
#include <stdio.h>

void print_ints(const int* arr, int count)
{
   for (int i=0; i<count; ++i)
   {
      if (i)
         putchar(' ');

      printf("%3d", arr[i]);
   }

   putchar('\n');
}

class Compar_Base
{
public:
   virtual ~Compar_Base()              { }
   virtual int comp(const void* left, const void* right) const = 0;

   static int compare(const void* left, const void* right, void* obj)
   {
      return static_cast<Compar_Base*>(obj)->comp(left,right);
   }
};

template <typename Func>
class Compar_Concrete : public Compar_Base
{
   Func &m_f;
public:
   Compar_Concrete(Func f) : m_f(f) { }
   virtual ~Compar_Concrete()       { }
   Compar_Concrete(const Compar_Concrete&) = delete;
   Compar_Concrete& operator=(const Compar_Concrete&) = delete;
   virtual int comp(const void* left, const void* right) const
   {
      return m_f(left,right);
   }
};

/** Shiny, new qsort for lambda expressions. */
template <typename Func>
void qsort_l(void *base, size_t member_count, size_t member_size, Func f)
{
   Compar_Concrete<Func> comp(f);
   qsort_r(base, member_count, member_size, Compar_Base::compare, &comp);
}

void sort_ascending(int* intlist, int count)
{
   auto f = [](const void* left, const void* right) -> int
   {
      return *static_cast<const int*>(left) - *static_cast<const int*>(right);
   };

   qsort_l(intlist, count, sizeof(int), f);
}

void sort_descending(int* intlist, int count)
{
   int testcount = 0;
   auto f = [&testcount](const void* left, const void* right) -> int
   {
      // Segmentation fault at this line:
      ++testcount;

      return *static_cast<const int*>(right) - *static_cast<const int*>(left);
   };

   qsort_l(intlist, count, sizeof(int), f);

   printf("\nIt took %d tests to complete the sort.\n", testcount);
}


int main(int argc, char** argv)
{
   int ilist[] = {1,9,2,8,3,7,4,6,5};
   int count = sizeof(ilist) / sizeof(int);

   print_ints(ilist,count);
   sort_ascending(ilist, count);
   print_ints(ilist,count);
   sort_descending(ilist, count);
   print_ints(ilist,count);
}

我使用g ++版本4.8.4和5.4.0编译了上面的代码,结果相同(即段错误)。您可以通过查看代码清单顶部声明的compile-command变量来查看正在使用的编译器选项。

1 个答案:

答案 0 :(得分:3)

Func &m_f;

这是一个类成员,是一个参考。这是你的构造函数:

Compar_Concrete(Func f) : m_f(f) { }

m_f成员初始化为对构造函数f参数的引用。

不幸的是,这个参数是按值传递的。当构造函数结束并终止时,f超出范围并被销毁,在类成员中留下悬空引用。

m_f的后续使用最终取消引用无效引用,并导致未定义的行为。这至少是一个明显的错误。

整个代码中充满了类型不安全的通用void *,它们可能也隐藏了一些其他错误,但是在寻找更多错误时没有意义,因为这几乎已经是表明,塞。