Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Connect with lambda and capture by reference causes segmentation fault



  • Hi,

    I'm wondering why capturing by reference in a connect lambda causes a segmentation fault.

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.5)
    project(test_qt LANGUAGES CXX)
    add_compile_options(-Wall -Wextra)
    set(CMAKE_CXX_STANDARD 14)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    
    set(CMAKE_AUTOMOC ON)
    find_package(Qt5 COMPONENTS Widgets REQUIRED)
    
    add_executable(
      ${PROJECT_NAME}
      mainwindow.hpp
      mainwindow.cpp
      main.cpp
    )
    target_link_libraries(
      ${PROJECT_NAME}
      Qt5::Widgets
    )
    

    main.cpp

    #include "mainwindow.hpp"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
      QApplication a(argc, argv);
      MainWindow w;
      w.show();
      return a.exec();
    }
    

    mainwindow.hpp

    #ifndef MAINWINDOW_HPP
    #define MAINWINDOW_HPP
    
    #include <QLabel>
    #include <QMainWindow>
    #include <QPushButton>
    #include <QVBoxLayout>
    
    class MainWindow : public QMainWindow
    {
      Q_OBJECT
    
    public:
      MainWindow(QWidget *parent = nullptr);
      ~MainWindow();
    };
    
    #endif
    

    mainwindow.cpp

    #include "mainwindow.hpp"
    
    MainWindow::MainWindow(QWidget *parent)
      : QMainWindow(parent)
    {
      QWidget *widget(new QWidget);
      setCentralWidget(widget);
      QVBoxLayout *layout(new QVBoxLayout(widget));
    
      QLabel *label(new QLabel("QLabel"));
      QPushButton *change_label(new QPushButton("Change label"));
    
      layout->addWidget(label);
      layout->addWidget(change_label);
    
      connect(change_label, &QPushButton::clicked, this, [&]() // FIXME Capture by reference = crash, capture by copy is ok!
      {
        label->setText("Hello");
      });
    }
    
    MainWindow::~MainWindow()
    {
    }
    

    Is it explicitly forbidden in Qt to capture by reference in a connect lambda?

    • Kubuntu 18.04
    • Qt 5.9.5
    • gcc 9.3.0
    • CMake 3.19.3

    gdb backtrace:

    (gdb) run
    Starting program: /home/victor/Documents/code/tests/qt/build/test_qt test_qt
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    [New Thread 0x7fffebd72700 (LWP 31414)]
    [New Thread 0x7fffdc88a700 (LWP 31415)]
    
    Thread 1 "test_qt" received signal SIGSEGV, Segmentation fault.
    0x00007ffff6f67b78 in operator==(QString const&, QString const&) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
    (gdb) bt
    #0    0x00007ffff6f67b78 in operator==(QString const&, QString const&) + 0x8 () at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
    #1    0x00007ffff78183f7 in QLabel::setText(QString const&) + 0x37 () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #2    0x0000555555556964 in MainWindow::<lambda()>::operator()(void) const (__closure=0x5555558497d0)
                             at /home/victor/code/tests/qt/src/mainwindow.cpp:18
    #3    0x0000555555556eee in QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, MainWindow::MainWindow(QWidget*)::<lambda()> >::call(MainWindow::<lambda()> &, void **) (f=@0x5555558497d0, arg=0x7fffffffcd50)
                             at /usr/include/x86_64-linux-gnu/qt5/QtCore/qobjectdefs_impl.h:130
    #4    0x0000555555556ec0 in QtPrivate::Functor<MainWindow::MainWindow(QWidget*)::<lambda()>, 0>::call<QtPrivate::List<>, void>(MainWindow::<lambda()> &, void *, void **) (f=@0x5555558497d0, arg=0x7fffffffcd50)
                             at /usr/include/x86_64-linux-gnu/qt5/QtCore/qobjectdefs_impl.h:240
    #5    0x0000555555556e8e in QtPrivate::QFunctorSlotObject<MainWindow::MainWindow(QWidget*)::<lambda()>, 0, QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void **, bool *) (which=1, this_=0x5555558497c0, r=0x7fffffffda30, a=0x7fffffffcd50, ret=0x0)
                             at /usr/include/x86_64-linux-gnu/qt5/QtCore/qobject_impl.h:168
    #6    0x00007ffff70fa66f in QMetaObject::activate(QObject*, int, int, void**) + 0x97f () at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
    #7    0x00007ffff77d1ba2 in QAbstractButton::clicked(bool) + 0x42 () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #8    0x00007ffff77d1dba in No symbol matches 0x00007ffff77d1dba. () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #9    0x00007ffff77d319a in No symbol matches 0x00007ffff77d319a. () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #10   0x00007ffff77d338d in QAbstractButton::mouseReleaseEvent(QMouseEvent*) + 0xfd () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #11   0x00007ffff771f048 in QWidget::event(QEvent*) + 0x1f8 () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #12   0x00007ffff76e083c in QApplicationPrivate::notify_helper(QObject*, QEvent*) + 0x9c () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #13   0x00007ffff76e865f in QApplication::notify(QObject*, QEvent*) + 0x7ff () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #14   0x00007ffff70cb8d8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) + 0x118 () at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
    #15   0x00007ffff76e7632 in QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*, QWidget*, QWidget**, QPointer<QWidget>&, bool) + 0x1d2 ()
                             at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #16   0x00007ffff773a16b in No symbol matches 0x00007ffff773a16b. () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #17   0x00007ffff773c7da in No symbol matches 0x00007ffff773c7da. () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #18   0x00007ffff76e083c in QApplicationPrivate::notify_helper(QObject*, QEvent*) + 0x9c () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #19   0x00007ffff76e8104 in QApplication::notify(QObject*, QEvent*) + 0x2a4 () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
    #20   0x00007ffff70cb8d8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) + 0x118 () at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
    #21   0x00007ffff5dfc583 in QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*) + 0x6f3 ()
                             at /usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
    #22   0x00007ffff5dfe055 in QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) + 0x135 ()
                             at /usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
    #23   0x00007ffff5dd52eb in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 0xab ()
                             at /usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
    #24   0x00007fffefe55260 in No symbol matches 0x00007fffefe55260. () at /usr/lib/x86_64-linux-gnu/libQt5XcbQpa.so.5
    #25   0x00007ffff45e8417 in g_main_context_dispatch + 0x2e7 () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
    #26   0x00007ffff45e8650 in No symbol matches 0x00007ffff45e8650. () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
    #27   0x00007ffff45e86dc in g_main_context_iteration + 0x2c () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
    #28   0x00007ffff712488f in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 0x5f ()
                             at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
    #29   0x00007ffff70c990a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 0x13a () at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
    #30   0x00007ffff70d29b4 in QCoreApplication::exec() + 0x94 () at /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
    #31   0x0000555555557252 in main(int, char**) (argc=2, argv=0x7fffffffdb68) at /home/victor/code/tests/qt/src/main.cpp:9
    
    

    Bye


  • Moderators

    @VictorLamoine
    i guess the reason for this behavior is that your label pointer variable is deleted when it goes out of scope.
    i am talking about the pointer variable holding the address, not the actual QLabel instance. If you capture it by value a new pointer variable lives inside the scope of your lambda.
    Btw. the default capture type is also by value not by reference.



  • That's right!

    If instead the widgets are declared private in the class:

    class MainWindow : public QMainWindow
    {
      Q_OBJECT
    
    public:
      MainWindow(QWidget *parent = nullptr);
      ~MainWindow();
    
    private:
      QLabel *label_;
      QPushButton *change_label_;
    };
    

    Then using a connect / lambda with capture by reference works fine:

    MainWindow::MainWindow(QWidget *parent)
      : QMainWindow(parent)
    {
      QWidget *widget(new QWidget);
      setCentralWidget(widget);
      QVBoxLayout *layout(new QVBoxLayout(widget));
    
      label_ = new QLabel("QLabel");
      change_label_ = new QPushButton("Change label");
    
      layout->addWidget(label_);
      layout->addWidget(change_label_);
    
      connect(change_label_, &QPushButton::clicked, this, [&]() // Capture by reference works!
      {
        label_->setText("Hello");
      });
    }
    

    The problem was indeed the scope of the variable.


Log in to reply