Better_Software_Header_MobileBetter_Software_Header_Web

Find what you need - explore our website and developer resources

QObjects, Ownership, propagate_const and C++ Evolution

Const Correctness in Qt Applications

class MyWidget : public QWidget {
    Q_OBJECT
public:
    explicit MyWidget(QWidget *parent = nullptr);

private:
    DataModel *m_dataModel;
    QTreeView *m_view;
    QLineEdit *m_searchField;
};
class MyWidget : public QWidget {
    Q_OBJECT
public:
    explicit MyWidget(QWidget *parent = nullptr);
 
private:
    DataModel *m_dataModel;
    QTreeView *m_view;
    QLineEdit *m_searchField;
};
 
MyWidget::MyWidget(QWidget *parent)
  : QWidget(parent)
{
    // Children parented to `this`
    m_dataModel = new DataModel(this);   
    m_view = new QTreeView(this);          
    m_searchField = new QLineEdit(this); 
}
class MyWidget : public QWidget {
    Q_OBJECT
public:
    explicit MyWidget(QWidget *parent = nullptr);
 
private:
    DataModel m_dataModel; // not pointers
    QTreeView m_view;
    QLineEdit m_searchField;
};
 
MyWidget::MyWidget(QWidget *parent)
  : QWidget(parent)
{
}
class MyObject : public QObject {
    Q_OBJECT
    QTimer m_timer;              // timer as sub-object
public:
    MyObject(QObject *parent)
      : QObject(parent)
      , m_timer(this)            // remember to do this...!
    {}
};
 
MyObject *obj = new MyObject;
obj->moveToThread(anotherThread); // ...or this will likely break
class MyWidget : public QWidget {
    Q_OBJECT
    QLineEdit m_searchField; // sub-object
public:
    explicit MyWidget(QWidget *parent = nullptr)
        : QWidget(parent)
    {
    }

    void update() const {
        m_searchField.setText("Search"); // ERROR
    }
};
class MyWidget : public QWidget {
    Q_OBJECT
    QLineEdit *m_searchField; // pointer
public:
    explicit MyWidget(QWidget *parent = nullptr)
        : QWidget(parent)
    {
        m_searchField = new QLineEdit(this);
    }

    void update() const {
        m_searchField->setText("Search");
    }
};
// Non-const pointer, pointing to non-const object
QLineEdit *p1 = ~~~;
  p1 = new QLineEdit;               // OK, can mutate the pointer
  p1->mutate();                     // OK, can mutate the pointed-to object

// Const pointer, pointing to non-const object
QLineEdit *const p2 = ~~~;
  p2 = new QLineEdit;               // ERROR, cannot mutate the pointer
  p2->mutate();                     // OK, can mutate the pointed-to

// Non-const pointer, pointing to const object
const QLineEdit *p3 = ~~~;
  p3 = new QLineEdit;               // OK, can mutate the pointer
  p3->mutate();                     // ERROR, cannot mutate the pointed-to object

// Non-const pointer, just like p3, but using East Const (Qt uses West Const)
QLineEdit const *p3b = ~~~;         

// Const pointer, pointing to const object
const QLineEdit * const p4 = ~~~ ;
  p4 = new QLineEdit;               // ERROR, cannot mutate the pointer
  p4->mutate();                     // ERROR, cannot mutate the pointed-to object

// Const pointer, just like p4, using East Const
QLineEdit const * const p4b = ~~~;
// Non-const wrapper => non-const pointed-to object
propagate_const<QLineEdit *> p1 = ~~~;
  p1->mutate();  // OK

// Const wrapper => const pointed-to object
const propagate_const<QLineEdit *> p2 = ~~~;
  p2->mutate();  // ERROR

// Const reference to the wrapper => const pointed-to object
const propagate_const<QLineEdit *> & const_reference = p1;
  const_reference->mutate(); // ERROR
class MyWidget : public QWidget {
    Q_OBJECT
    std::experimental::propagate_const<QLineEdit *> m_searchField;  // drop-in replacement

public:
    explicit MyWidget(QWidget *parent = nullptr)
        : QWidget(parent)
    {
        m_searchField = new QLineEdit(this);
    }

    void update() const {
        m_searchField->clear(); // ERROR!
    }
};
class MyWidget : public QWidget {
    Q_OBJECT
    QLineEdit *m_searchField;

public:
    explicit MyWidget(QWidget *parent = nullptr)
        : QWidget(parent)
    {
        // ... set up layouts, etc. ...
        m_searchField = new QLineEdit(this);
        layout()->addWidget(m_searchField); // this is addWidget(QWidget *)
    }
};
class MyWidget : public QWidget {
    Q_OBJECT
    // change to propagate_const ...
    std::experimental::propagate_const<QLineEdit *> m_searchField;  

public:
    explicit MyWidget(QWidget *parent = nullptr)
        : QWidget(parent)
    {
        // ... set up layouts, etc. ...
        m_searchField = new QLineEdit(this);
        layout()->addWidget(m_searchField);
        // ^^^ ERROR, cannot convert propagate_const to QWidget *
    }
};

operator value*

std::experimental::propagate_const<Derived *> ptr;

Derived *d1 = ptr;  // Convert precisely to Derived *: OK
Base *b1    = ptr;  // Convert to a pointer to a base: ERROR

Base *b2    = static_cast<Derived *>(ptr); // OK
Base *b3    = static_cast<Base *>(ptr);    // ERROR

Base *b4    = ptr.get();  // OK
template <typename T>
class propagate_const
{
public:
    using element_type
        = std::remove_reference_t<decltype(*std::declval<T>())>;

    operator element_type *();
    operator const element_type *() const;
};

propagate_const<Derived *> ptr;
Base *b = ptr; // OK
template <typename T>
class propagate_const
{
public:
    using element_type
        = std::remove_reference_t<decltype(*std::declval<T>())>;

    operator element_type *()
      requires (std::is_pointer_v<T>
                || std::is_convertible_v<T, element_type *>);

    operator const element_type *() const
      requires (std::is_pointer_v<T>
                || std::is_convertible_v<const T, const element_type *>);
};

propagate_const<Derived *> ptr;
Base *b = ptr;    // still OK
template <typename T>
class propagate_const
{
public:
    using element_type = std::remove_reference_t<decltype(*std::declval<T>())>;

    template <typename U = T,
      std::enable_if_t<(std::is_pointer_v<U>
                       || std::is_convertible_v<U, element_type *>),
        bool> = true>
    operator element_type *();

    template <typename U = T,
      std::enable_if_t<(std::is_pointer_v<U>
                       || std::is_convertible_v<const U, const element_type *>),
        bool> = true>
    operator const element_type *() const;
};

propagate_const<Derived *> ptr;
Base *b = ptr;    // ERROR
template <typename T>
class non_standard_propagate_const  // not the Standard one!
{
public:
    using element_type = std::remove_reference_t<decltype(*std::declval<T>())>;

    // Convert to "any" pointer type
    template <typename U,
      std::enable_if_t<std::is_convertible_v<T, U *>, bool> = true>
    operator U *();

    template <typename U,
      std::enable_if_t<std::is_convertible_v<const T, const U *>, bool> = true>
    operator const U *() const;
};

non_standard_propagate_const<Derived *> ptr;
Base *b = ptr;    // OK
KDToolbox.png
// Non-const conversion
template <typename T,
          bool = std::disjunction_v<
              std::is_pointer<T>,
              std::is_convertible<T, propagate_const_element_type<T> *>
              >
          >
struct propagate_const_non_const_conversion_operator_base
{
};

template <typename T>
struct propagate_const_non_const_conversion_operator_base<T, true>
{
    constexpr operator propagate_const_element_type<T> *();
};
template <typename T>
class propagate_const
{
public:
    using element_type
        = std::remove_reference_t<decltype(*std::declval<T>())>;

    operator element_type *()
      requires (std::is_pointer_v<T>
                || std::is_convertible_v<T, element_type *>);

    operator const element_type *() const
      requires (std::is_pointer_v<T>
                || std::is_convertible_v<const T, const element_type *>);
};

propagate_const<SomeClass *> ptr;
SomeClass *ptr2 = ptr + 1; // OK!
template <typename T>
class propagate_const
{
public:
    using element_type
        = std::remove_reference_t<decltype(*std::declval<T>())>;

    operator element_type *()
      requires (std::is_pointer_v<T>
                || std::is_convertible_v<T, element_type *>);

    operator const element_type *() const
      requires (std::is_pointer_v<T>
                || std::is_convertible_v<const T, const element_type *>);
};

propagate_const<SomeClass *> ptr;

delete ptr; // ERROR on GCC, Clang; OK on MSVC
propagate_const<SomeClass *> ptr;

delete +ptr; // OK! (ewww...)
propagate_const<std::unique_ptr<SomeClass>> ptr;  // better

About KDAB


1 Comment

13 - Feb - 2023

skierpage

GiuseppeD'Angelo

Giuseppe D’Angelo

Senior Software Engineer

Learn Modern C++

Learn more