簡體   English   中英

為什么訪問我的按引用捕獲變量會導致我的lambda函數出現段錯誤?

[英]Why does accessing my capture-by-reference variable cause a segfault in my lambda function?

我將針對本地C / C ++ Meetup進行關於lambda表達式的演示,因此,我准備一些示例來演示lambda表達式如何解決某些任務。

示例之一是創建一個qsort函數,該函數使用lambda表達式作為比較函數。 對於下面的sort_ascending()這種簡單情況,它可以很好地工作,但是對於sort_descending() ,其中我包含一個計數器整數,該整數被引用捕獲,並隨每次對lambda表達式的調用而遞增,因此在第一次嘗試遞增時會遇到段錯誤櫃台。

我看了我可以找到的lambda表達式文檔,尤其是cppreference.cpp 據我所知,沒有理由為什么testcount中的sort_descending不在范圍之內: testcount與對qsort_l的調用位於同一堆棧幀中,並且當qsort_l返回時不再使用捕獲testcount的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編譯了上面的代碼,結果相同(即segfault)。 通過查看代碼清單頂部聲明的compile-command變量,可以看到正在使用的編譯器選項。

Func &m_f;

這是一個類成員,它是參考。 這是您的構造函數:

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

m_f成員被初始化為對構造函數的f參數的引用。

不幸的是,該參數按值傳遞。 當構造函數結束並終止時, f超出范圍並被銷毀,從而在類成員中留下了懸掛的引用。

隨后使用m_f最終會取消引用無效的引用,並導致未定義的行為。 那至少是一個明顯的錯誤。

整個代碼充滿了類型不安全的泛型void * ,它們可能也隱藏了其他一些bug,但是尋找更多的bug毫無意義,因為這已經很容易成為問題了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM