using namespace std;

Olivier Goffart
http://woboq.com

This Presentation

Using both Qt and the standard library

  • Overview of the differences
  • Algorithms
  • Containers
  • Others

About Me

  • KDE
  • Trolltech/Nokia
  • Woboq GmbH

About Me

  • Maintained QStyleSheetStyle.
  • Worked on Itemview and other widgets.
  • Ported QtScript to JavascriptCore and V8.
  • Designed and implemented the new connection syntax.
  • Maintain moc
  • ...

History

  • 1996: Qt 1.0
  • 1998: First ISO C++ standard
  • 1998: Microsoft Visual Studio 6.0.
  • 2005: Qt 4.0
  • 2012: Qt 5.0
  • Qt 4 still supported MSVC 6, Sun CC, HP aC++.
  • Qt 4 could compile without the STL.
  • Qt 5 uses more of the STL internally, but does not expose it in its ABI

ABI (Application Binary Interface)

QLineEdit::setText(const QString &)
  • _ZN9QLineEdit7setTextERK7QString


QLineEdit::setText(const std::string &)
  • GNU's libstdc++:
    _ZN9QLineEdit7setTextERKSs
  • LLVM's libc++:
    _ZN9QLineEdit7setTextERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
Qt does not expose the standard library in its ABI:

☺ Qt compiled against one implementation is binary compatible with applications compiled against another implementation.

☹ Qt has to re-invent the wheel.

Qt vs. STL

Main Differences

Coding style

Goals

STL

General purpose. Designed for everything.


Qt

Focus on making C++ easy to use for a GUI application.

QMap<Key, T>

std::map<Key, T, Compare, Allocator>


QHash<Key, T >

std::unordered_map<Key, T, Hash, KeyEqual, Allocator>


std::basic_string<CharT, Trait, Alloc>

Naming Conventions

Qt STL
count, size, length size
append, push_back
operator+=, operator<<
push_back
constBegin, cbegin cbegin
operator[]
at
operator[]
at (*)
value
Qt STL
last, back back
first, front front
prepend, push_front push_front
removeFirst, pop_front pop_front
takeFirst
isEmpty, empty empty
indexOf — (std::find)
...

Hint: When mixing Qt code and the standard library,
prefer the STL style

Attention: beware of the differences in at and []

  • Qt's non-const operator[] might copy ⇒ prefer at()
  • Std at() does runtime bound check ⇒ prefer []

Implicit Sharing

(Copy on Write)


QString str = "Hello World"; //1
QString str2 = str;          //2
str.append("!");             //3

Algorithms

qSort(myVect);

    std::sort(myVect.begin(), myVect.end());

Algorithms

All standard algorithms work with Qt containers.


QVector<Item> items = getMyList();
auto it = items.begin();
while(it != it.end()) {
    if (it->priority() <= threshold)
        it = items.erase(it);
    else
        ++it;
}

 


QVector<Item> items = getAllItems();
auto it = std::remove_if(items.begin(), items.end(),
        [&](const Item &it) {
            return it.priority() <= threshold;
        }));
items.erase(it, items.end());


const QVector<Item> items = getAllItems();
const QVector<Item> result;
foreach (const Item &it, items) {
  if (it.priority() > threshold)
     result.append(it);
}



const QVector<Item> items = getAllItems();
const QVector<Item> result;
std::copy_if(items.begin(), items.end(),
             std::back_inserter(result),
             [&](const Item &it) {
                return it.priority() > threshold;
             });

More Algorithms

http://en.cppreference.com/w/cpp/algorithm



Use standard algorithms instead of rolling your own loop.

Ranges (C++17?)


    std::sort(myVect);




http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0021r0.pdf
http://ericniebler.github.io/range-v3

Containers

Sequential Containers

Qt STL
QVector std::vector
std::deque
QList
QLinkedList std::list
std::forward_list
QVarLengthArray
experimental::dynarray
std::array

QVector vs. std::vector



Q_DECLARE_TYPEINFO


Q_DECLARE_TYPEINFO(MyType, Q_MOVABLE_TYPE);

  • Q_PRIMITIVE_TYPE specifies that Type is a type with no constructor or destructor
  • Q_MOVABLE_TYPE specifies that Type can be moved in memory using memcpy().
  • Q_COMPLEX_TYPE (the default) specifies that Type may not be moved in memory.
The Qt Documentation

QList

No indirections if:

  • sizeof(T) <= sizeof(void*)
  • and Q_DECLARE_TYPEINFO(T, Q_MOVABLE_TYPE)

std::deque

QVarLengthArray<T,Prealloc>

Warning: Default Prealloc = 256

Sequential Containers

  • Use QVarLengthArray on the stack for usually small lists
  • Use QVector or std::vector if you can use reserve(), or need the elements contiguous.
  • Use std::deque if you need to insert elements at the begining.

Avoid QList unless you pass it to Qt API.

Associative Containers

Qt STL
QMap std::map
QMultiMap std::multimap
std::set, std::multiset
QHash std::unordered_map
QMultiHash std::unordered_multimap
QSet std::unordered_set
std::unordered_multiset

Qt's foreach

  • Makes a copy of the container
    • Ok with implicitly shared containers.
    • Expensive otherwise.
    • Can modify (add, remove, ...) the original container.

C++11 for(:)

  • Calls begin() and end()
    • Detach implicitly shared containers (unless const)
  • Can take references to the items and modify them.
foreach         for(:)
std::vector Slow¹ OK
QVector OK Attention²
QVarLengthArray Slow¹ OK


  1. foreach makes a copy.
  2. Might detach.

Attention: Don't use the standard container in Qt's foreach.


Hint: Use Qt containers in for(:) only if const or not shared.


#define AS_CONST(OBJ) const_cast<const decltype(OBJ)&>(OBJ)

  for(const auto &val : AS_CONST(getQVector())) /* ... */

String Types

Qt STL
QByteArray std::string
QString std::wstring, std::u16string
QStringRef
experimental::string_view

Qt's good unicode support

 


    QString(u"größte").toUpper();  // "GRÖSSTE"

 



std::u16string str = u"größte";
std::transform(str.begin(), str.end(),str.begin(),
               towupper);
    // "GRÖßTE"
   

string_view


bool isValidIdentifier(const QByteArray &id)
{
    return d.size() > 3 && !id.endsWith("_w");
}

QByteArray myQBA = qmlView->getToken();
if (isValidIdentifier(myQBA)) {/* ... */}

std::string myStdStr = answomeLib->getId();
// Allocates!
if (isValidIdentifier(QByteArray::fromStdString(myStdStr))
{ /* ... */ }

Solution 1


template<typename T>
bool isValidIdentifier(const T &id);
    

Solution 2


    bool isValidIdentifier(const char*);
    

string_view


    bool isValidIdentifier(const string_view &id);
    

Smart Pointers

Qt STL
QScopedPointer
std::unique_ptr
QSharedPointer std::shared_ptr
QWeakPointer std::weak_ptr
QSharedDataPointer
QExplicitlySharedDataPointer

// Does this function take ownership of the pointer?
QAction *QMenu::addMenu(QMenu *);
void QGraphicsScene::addItem(QGraphicsItem *item);

// Do I have to delete the returned pointer ?
QLayoutItem * QLayout::takeAt(int index)



// Yes, takes ownership
void QGraphicsScene::addItem(std::unique_ptr<QGraphicsItem> item);

// Gives ownership
std::unique_ptr<QLayoutItem> QLayout::takeAt(int index);

Smart Pointers

  • QScopedPointer is not movable (by design)
  • std::vector<std::unique_ptr<FooBar>> impossible with QVector or QScopedPointer

Threads

Should you subclass QThread?

You can also use std::thread!

  • Qt will automatically create a QThread for it
  • All the Qt threading feature works in std::thread (Signals-slots, QMutex, QThreadStorage, ...)
  • std::mutex, . also work in QThread

class MyObject : public QObject {
/*...*/
    std::thread worker;
signals:
    void resultReady(quint64);
};

void MyObject::startWorkInAThread() {
    Q_ASSERT(!m_worker.joinable()); // Already running?
    m_worker = std::thread( [=] {
        auto res = fib(n); /* expensive or blocking */
        emit this->resultReady(res);
    } );

    QObject::connect(this, &MyObject::resultReady,
                     this, [=](quint64 val) {
        qDebug() << "Result:"  << val;
        this->m_worker.join();
    });
}

m_thread = std::thread( [=] {
  QEventLoop loop;
  WorkerObj worker;
  QObject::connect(&worker, &WorkerObj::resultReady,
                   this, &MyObject::resultReady);
  QObject::connect(&worker, &WorkerObj::resultReady,
                   QThread::currentThread(), &QThread::quit);
  worker.start();
  loop.exec();
});

More

  • File system TS based on boost::filesystem (C++17)
  • Network library based on boost::asio (C++17)

Future of Qt.

  • Qt6 will probably remove its already deprecated algorithms
  • Should Qt6 get rid of it's containers?

moc

  • Orthogonal to the use of the standard library
  • Allow nicer syntax
  • Maybe someday reflection comes in C++?
  • moc is here to stay for a long time

Conclusion

  • Don't be afraid of the standard library

Questions


Olivier Goffart

olivier@woboq.com

Twitter: @woboq

http://woboq.com

QStringLiteral


std::string s1 = "Lorem ipsum dolor sit"; // allocates
std::u16string s2 = "Lorem ipsum dolor sit"; // allocates
QString s3 = "Lorem ipsum dolor sit"; // allocates
QByteArray s4 = "Lorem ipsum dolor sit"; // allocates

 
// no allocations:
QString s5 = QStringLiteral("Lorem ipsum dolor sit");
QByteArray s6= QByteArrayLiteral("Lorem ipsum dolor sit");

QStringBuilder


QString myString = "Head: " + head
                 + "\nBody: " + body + '\n';


-D QT_USE_QSTRINGBUILDER

or


QString myString = "Head: " % head
                 % "\nBody: " % body % '\n';