C++11 default destructor and std::unique_ptr
-
Hi!
I'm playing around with C++11 features to improve my Qt programs which by now rely mostly on C++98.
I have a QWidgets application with a QMainWindow derived MainWindow and a Ui::MainWindow generated with Qt Designer. Now I tried to be super smart and instead of keeping a pointer
Ui::MainWindow *ui;
I keep astd::unique_ptr
to the ui. Thus I don't need the usual~MainWindow()
withdelete ui;
inside anymore. This is what the code looks like:mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <memory> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); private: std::unique_ptr<Ui::MainWindow> m_ui; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , m_ui(new Ui::MainWindow) { m_ui->setupUi(this); }
That was my naive theory. Turns out it doesn't compile. The error says:
/usr/include/c++/4.8/bits/unique_ptr.h:-1: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Ui::MainWindow]': /usr/include/c++/4.8/bits/unique_ptr.h:184: required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Ui::MainWindow; _Dp = std::default_delete<Ui::MainWindow>]' /usr/include/c++/4.8/bits/unique_ptr.h:65: error: invalid application of 'sizeof' to incomplete type 'Ui::MainWindow' static_assert(sizeof(_Tp)>0, ^
I tried adding
~MainWindow() = default;
but that didn't help. Can someone please explain to me what's going on here? -
@Wieland
#include "ui_mainwindow.h"
in the header, not only in source?
:) -
@kshegunov Hi! Before we move to the question why the forward declaration of Ui::MainWindow in the header might not be sufficient: Why doesn't the
~MainWindow() = default;
work? Because, as soon as I add~MainWindow();
to the header andMainWindow::~MainWindow() {}
to the source it compiles. -
@Wieland
Funny thing with templates. Instantiation happens where you do the specialization (in this case the header). The compiler needs the members to be fully defined to generate the default destructor (i.e. it needs to know the destructor ofUI::MainWindow
in this case and this is happening while parsing the header no less).When you provide your own destructor (in source) the compiler doesn't need to call the destructor of the
unique_ptr
class (thus the destructor of theUi::MainWindow
class) and you're fine only using forward declarations :)I hope I explained it understandably.
:)
-
@kshegunov Ah.. sounds logical. Thank you!
-
@Wieland
My pleasure! :D -
If someone needs a comprehensive 8 pages long explanation for what's exactly happening here, I recommend reading "Item 22: When using the Pimpl Idiom, define special member functions in the implementation file" from "Effective Modern C++" by Scott Meyers.