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
-
@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.