c++ - Why does accessing my capture-by-reference variable cause a segfault in my lambda function? -
i going give presentation on lambda expressions our local c/c++ meetup, preparing several examples demonstrate how lambda expressions can solve tasks.
one of examples create qsort function takes lambda expression comparison function. works fine simple case of sort_ascending() below, sort_descending(), include counter integer captured reference , incremented each call lambda expression, segfault @ first attempt increment counter.
i've looked @ lambda expression documentation find, cppreference.cpp. far can tell, there's no reason why testcount in sort_descending should out-of-scope: testcount in same stack frame call qsort_l, , lambda expression captured testcount no longer used when qsort_l returns.
// -*- 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) { (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 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 @ 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 complete 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); } i compiled code above using g++ versions 4.8.4 , 5.4.0 same result (ie segfault). can see compiler options in use looking @ compile-command variable declared @ top of code listing.
func &m_f; this class member, reference. here's constructor:
compar_concrete(func f) : m_f(f) { } the m_f member initialized reference constructor's f parameter.
unfortunately, parameter gets passed value. when constructor ends , terminates, f goes out of scope , gets destroyed, leaving dangling reference in class member.
subsequent usage of m_f ends dereferencing invalid reference, , results in undefined behavior. that's @ least 1 obvious bug.
the entire code full of type-unsafe generic void * hiding few other bugs well, there's no point in looking more bugs, when pretty show-stopper.
Comments
Post a Comment