Qt Containers and Internationalization


  • Description: Qt's container family (QList/QVector/QHash/QMap/QSet/QStack/QQueue), implicit sharing (COW), STL interop, QString and QByteArray, QVariant, and the i18n pipeline (tr + lupdate + Qt Linguist + QTranslator)
  • My Notion Note ID: K2A-B3-7
  • Created: 2018-03-04
  • Updated: 2026-05-18
  • License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io

Table of Contents


1. Sequential Containers

Container Behavior Notes
QList<T> Dynamic array. In Qt 6, QList and QVector are the same type (QList is an alias for QVector). Default choice for sequences.
QVector<T> Dynamic array. Kept for compatibility. Identical to QList in Qt 6.
QVarLengthArray<T, N> Stack-allocated for size ≤ N, heap above. Cheap small-N optimization.
QStack<T> LIFO. Adds push/pop/top to QList.
QQueue<T> FIFO. Adds enqueue/dequeue/head to QList.
QLinkedList<T> Removed in Qt 6 — use std::list if you really need a linked list.
QList<int> nums = {1, 2, 3};
nums.append(4);
nums.prepend(0);
nums.removeAt(1);
int first = nums.first();
int last  = nums.last();
qsizetype n = nums.size();

QStack<QString> undo;
undo.push("typed 'a'");
QString last_op = undo.pop();

QQueue<Job> jobs;
jobs.enqueue(j);
Job next = jobs.dequeue();
  • The Qt 5-to-6 QList redesign was a real API change. Qt 5 QList<T> was a heap-allocated array of T*-or-T (depending on size); Qt 6 QList<T> is a contiguous T[]. Iterator invalidation rules now match std::vector: any insert/remove can invalidate.

2. Associative Containers

Container Ordered? Notes
QMap<K, V> Sorted by K Lookup O(log n). Useful when you need ordered iteration.
QMultiMap<K, V> Sorted, allows duplicate keys values(k) returns all values for key.
QHash<K, V> Unordered (hash table) Lookup O(1) amortized. Default choice unless you need ordered iteration.
QMultiHash<K, V> Unordered, duplicates allowed
QSet<T> Unordered set Backed by QHash<T, ...>.
QHash<QString, int> counts;
counts["apples"]   = 3;
counts["oranges"]  = 5;
counts.insert("kiwi", 2);

if (counts.contains("apples")) {
    int n = counts.value("apples", 0);          // returns 0 if missing
}

for (auto it = counts.cbegin(); it != counts.cend(); ++it) {
    qDebug() << it.key() << "->" << it.value();
}

QSet<int> seen;
seen << 1 << 2 << 3;
seen.contains(2);                                // true
  • QHash requires qHash(const T&) overload (provided for built-in types and most Qt types — QString, QByteArray, QUrl, etc.). Custom keys need a user-defined qHash + operator==.
  • QHash is unordered AND iteration order is unstable across runs (Qt randomizes the seed to prevent hash-flooding attacks since 5.0). If tests depend on iteration order, sort the keys first.

3. Implicit Sharing (COW)

  • Qt's containers, QString, QByteArray, QPixmap, QImage, and most "value-like" types are implicitly shared: copying them only bumps a reference count; the underlying buffer is duplicated lazily on the first mutating operation.
QList<int> a = makeBigList();    // build
QList<int> b = a;                // O(1) — shares buffer
b.append(99);                    // detach + copy now, O(n)
  • Implications:
    • Pass-by-value is cheap, even for huge containers. You can return QList<T> from functions without worrying.
    • You don't need const QList<T> & for "input parameter you won't modify" — by-value is fine. (Style guides differ; const & is still common because it documents intent.)
    • Don't take a T* to internal storage and expect it to stay valid across a copy. The next mutation detaches and you're holding into a dead buffer.
QList<int> a = {1, 2, 3};
int *p = a.data();
QList<int> b = a;       // shares buffer
b[0] = 9;               // detach: b now points elsewhere; p still points at a's original
  • detach() forces a deep copy now if you want to guarantee uniqueness.

4. Iteration Styles

  • Three idioms, all common in Qt code:
QList<QString> names = ...;

// 1. Range-based for (C++11+)
for (const QString &n : names) {
    use(n);
}

// 2. STL-style iterators
for (auto it = names.cbegin(); it != names.cend(); ++it) {
    use(*it);
}

// 3. Java-style iterators — Qt-specific, less common in modern code
QListIterator<QString> i(names);
while (i.hasNext()) {
    use(i.next());
}
// Mutable variant
QMutableListIterator<QString> mi(names);
while (mi.hasNext()) {
    if (mi.next() == "drop") mi.remove();
}
  • Prefer the range-based loop. Java-style iterators (QListIterator, QMutableHashIterator, etc.) date from before C++11 lambdas; they still work but read as old-style.
  • foreach keyword — Qt's pre-C++11 Q_FOREACH(x, container) macro is discouraged in Qt 6 and can be turned off project-wide with QT_NO_FOREACH. Use range-for.

5. QString

  • Unicode (UTF-16 internally), implicitly shared. The everywhere-string of Qt code.
QString s = "hello";                              // UTF-8 → UTF-16
s.append(", world");
int n = s.length();                               // number of UTF-16 code units (not glyphs)
QString upper = s.toUpper();

// Formatting — prefer arg() over sprintf-style
QString line = QStringLiteral("user=%1 id=%2").arg(name).arg(id);

// Number conversions
int    x  = QString("42").toInt();
double d  = QString("3.14").toDouble();
QString z = QString::number(42);
QString w = QString::number(3.14, 'f', 2);        // "3.14"

// Substrings, search
bool has = s.contains("world");
int idx  = s.indexOf(',');
QString first = s.left(5);
QString rest  = s.mid(7);
QStringList parts = s.split(", ");
  • QStringLiteral("text") makes the string a compile-time constant — no runtime UTF-8 → UTF-16 conversion or heap allocation. Use it for any string literal you pass to a QString parameter.
  • length() counts UTF-16 code units, not user-perceived characters. Emoji and CJK extension B characters are 2 code units. For "glyph count", iterate QStringIterator over grapheme clusters.

6. QByteArray, QStringList, QVariant

  • QByteArray — sequence of char. Use for raw bytes (file contents, network buffers, hashes). Convert with QString::fromUtf8(bytes) and string.toUtf8().
QFile f("data.bin");
f.open(QIODevice::ReadOnly);
QByteArray bytes = f.readAll();
QByteArray hex = bytes.toHex();
QByteArray b64 = bytes.toBase64();
  • QStringList — typedef-ish for QList<QString>. Lots of string-aware conveniences: join, filter, replaceInStrings.
QStringList paths = QString("/a/b/c").split('/');     // {"", "a", "b", "c"}
QString rejoined = paths.join('/');
  • QVariant — type-erased container for any of Qt's "meta-types". Powers Q_PROPERTY, models (QAbstractItemModel::data returns QVariant), settings (QSettings).
QVariant v = 42;
qDebug() << v.typeName();           // "int"
int n = v.toInt();

v = QString("hello");
QString s = v.toString();

v = QPointF(1, 2);
auto pt = v.value<QPointF>();       // template form for non-builtin types
  • For custom types: Q_DECLARE_METATYPE(MyClass) + qRegisterMetaType<MyClass>() makes them storable in QVariant and queue-able across threads.

7. STL Interop and "Qt vs STL"

  • Qt containers and std containers are usually interchangeable. Qt provides STL-style iterators (begin, end, cbegin, cend), so they work with std::find, std::sort, range-for.
QList<int> qs = {3, 1, 4, 1, 5};
std::sort(qs.begin(), qs.end());

std::vector<int> v(qs.begin(), qs.end());        // construct std::vector from QList
QList<int> back(v.begin(), v.end());             // and back
  • Should you use Qt containers or std::?
    • Mixed Qt code (Qt models, signals/slots with container args, custom types in QVariant): use Qt containers. Saves marshaling. QHash is also faster than older std::unordered_map on many compilers (less of a difference with newer libcxx/libstdcxx).
    • Pure algorithmic code, libraries you'd want portable: std::vector, std::unordered_map, std::string. Avoid pulling all of Qt into a math library.
  • Avoid round-tripping std::stringQString per call inside a hot path — the UTF-8/UTF-16 conversion isn't free. Pick one and stick with it across the API boundary.

8. Internationalization

8.1 The tr Pipeline

  • tr("text") is a no-op at runtime if no translator is loaded — returns "text" unchanged. With a .qm loaded, it returns the translation.
class MyForm : public QWidget {
    Q_OBJECT
    void buildUi() {
        title_->setText(tr("Welcome"));
        ok_   ->setText(tr("&OK"));
    }
};
  • The tooling chain:
    1. lupdate myapp.pro (or lupdate -ts i18n/*.ts $(find src -name '*.cpp')) — scans source, writes myapp_en.ts, myapp_zh.ts, etc. Each tr call becomes a <message><source>...</source><translation>...</translation></message> entry.
    2. Translators open the .ts file in Qt Linguist and fill in translations.
    3. lrelease myapp.pro compiles .ts → binary .qm files.
    4. At runtime your app loads the .qm matching the user's locale.

8.2 QTranslator and Runtime Locale

QApplication app(argc, argv);

QTranslator translator;
const QString locale = QLocale::system().name();         // e.g. "zh_CN"
if (translator.load("myapp_" + locale, ":/i18n")) {
    app.installTranslator(&translator);
}

// Now every tr() returns the translation
MyMainWindow w;
w.show();
return app.exec();
  • installTranslator calls every existing QObject's changeEvent(QEvent::LanguageChange). To support live language switching at runtime, override changeEvent and re-call all your setText(tr(...)) lines.
  • For Qt's own dialog text (Save / Cancel / etc.) load Qt's bundled translation too:
QTranslator qt;
qt.load("qtbase_" + locale, QLibraryInfo::path(QLibraryInfo::TranslationsPath));
app.installTranslator(&qt);

8.3 Q_DECLARE_TR_FUNCTIONS

  • A class that's not a QObject doesn't have tr. Add it manually:
class ErrorTable {
    Q_DECLARE_TR_FUNCTIONS(ErrorTable)
public:
    QString message(int code) const {
        switch (code) {
            case 1: return tr("File not found");
            case 2: return tr("Permission denied");
        }
        return tr("Unknown error");
    }
};
  • The macro injects a static tr whose translation context is the class name. lupdate recognizes it the same as QObject::tr.

8.4 Plurals and Disambiguation

  • tr accepts an optional second arg for disambiguation (when the same English word has different translations in different contexts), and a third for plural count:
tr("Open", "menu item");        // disambiguation context
tr("Open", "verb in toolbar");

QString s = tr("%n file(s) selected", "", count);   // plural — "%n" replaced, "(s)" handled by translator
  • The plural form lets translators handle languages with multiple plural categories (Russian, Polish, Arabic). Qt Linguist shows separate fields for "singular", "dual", "few", "many", etc., per the target language's rules.

8.5 QLocale

  • Number/date/time/currency formatting per region.
QLocale us(QLocale::English, QLocale::UnitedStates);
QString s = us.toString(1234567.89, 'f', 2);            // "1,234,567.89"
QString d = us.toString(QDate::currentDate(), QLocale::ShortFormat);

QLocale system = QLocale::system();
qDebug() << system.name();                              // "en_US", "zh_CN", "ja_JP"
qDebug() << system.language();                          // QLocale::Chinese
  • QLocale::system() reflects the user's OS locale at the time of the call. Cache it at app start unless you support live language switching.

8.6 Text Codecs (Qt 5 vs Qt 6)

  • Qt 5 required QTextCodec for non-UTF-8 byte ↔ string conversions:
// Qt 5 only
#include <QTextCodec>
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
QString s = QTextCodec::codecForName("Shift-JIS")->toUnicode(bytes);
  • Qt 6 removed QTextCodec from QtCore. UTF-8 is assumed for the locale, and QString's constructors expect UTF-8 bytes by default. For legacy encodings, use QStringConverter / QStringDecoder / QStringEncoder:
// Qt 6
QStringDecoder fromShiftJis(QStringConverter::System);   // or explicit name
QStringDecoder fromGbk("GBK");
QString s = fromGbk(bytes);

QStringEncoder toUtf16 = QStringEncoder("UTF-16LE");
QByteArray out = toUtf16(s);
  • QStringConverter::Encoding enum lists the built-in encodings: UTF-8, UTF-16, UTF-16LE/BE, UTF-32 variants, Latin-1, System. Anything else needs the Qt5Compat module (Qt6::Core5Compat::QTextCodec) — Qt 6 keeps QTextCodec available as a compatibility shim.

9. References