K2A-B3-7
Qt Containers and Internationalization
Created 2018-03-04Updated 2026-05-18programming-language-stack / c_or_cpp / qt
- Description: Qt's container family (
QList/QVector/QHash/QMap/QSet/QStack/QQueue), implicit sharing (COW), STL interop,QStringandQByteArray,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
- 2. Associative Containers
- 3. Implicit Sharing (COW)
- 4. Iteration Styles
- 5.
QString - 6.
QByteArray,QStringList,QVariant - 7. STL Interop and "Qt vs STL"
- 8. Internationalization
- 9. References
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
QListredesign was a real API change. Qt 5QList<T>was a heap-allocated array ofT*-or-T(depending on size); Qt 6QList<T>is a contiguousT[]. Iterator invalidation rules now matchstd::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
QHashrequiresqHash(const T&)overload (provided for built-in types and most Qt types —QString,QByteArray,QUrl, etc.). Custom keys need a user-definedqHash+operator==.QHashis 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.
- Pass-by-value is cheap, even for huge containers. You can return
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. foreachkeyword — Qt's pre-C++11Q_FOREACH(x, container)macro is discouraged in Qt 6 and can be turned off project-wide withQT_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 aQStringparameter.length()counts UTF-16 code units, not user-perceived characters. Emoji and CJK extension B characters are 2 code units. For "glyph count", iterateQStringIteratorover grapheme clusters.
6. QByteArray, QStringList, QVariant
QByteArray— sequence ofchar. Use for raw bytes (file contents, network buffers, hashes). Convert withQString::fromUtf8(bytes)andstring.toUtf8().
QFile f("data.bin");
f.open(QIODevice::ReadOnly);
QByteArray bytes = f.readAll();
QByteArray hex = bytes.toHex();
QByteArray b64 = bytes.toBase64();
QStringList— typedef-ish forQList<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". PowersQ_PROPERTY, models (QAbstractItemModel::datareturnsQVariant), 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 inQVariantand queue-able across threads.
7. STL Interop and "Qt vs STL"
- Qt containers and
stdcontainers are usually interchangeable. Qt provides STL-style iterators (begin,end,cbegin,cend), so they work withstd::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.QHashis also faster than olderstd::unordered_mapon 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.
- Mixed Qt code (Qt models, signals/slots with container args, custom types in
- Avoid round-tripping
std::string↔QStringper 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.qmloaded, it returns the translation.
class MyForm : public QWidget {
Q_OBJECT
void buildUi() {
title_->setText(tr("Welcome"));
ok_ ->setText(tr("&OK"));
}
};
- The tooling chain:
lupdate myapp.pro(orlupdate -ts i18n/*.ts $(find src -name '*.cpp')) — scans source, writesmyapp_en.ts,myapp_zh.ts, etc. Eachtrcall becomes a<message><source>...</source><translation>...</translation></message>entry.- Translators open the
.tsfile in Qt Linguist and fill in translations. lrelease myapp.procompiles.ts→ binary.qmfiles.- At runtime your app loads the
.qmmatching 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();
installTranslatorcalls every existingQObject'schangeEvent(QEvent::LanguageChange). To support live language switching at runtime, overridechangeEventand re-call all yoursetText(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
QObjectdoesn't havetr. 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
trwhose translation context is the class name.lupdaterecognizes it the same asQObject::tr.
8.4 Plurals and Disambiguation
traccepts 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
QTextCodecfor 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
QTextCodecfromQtCore. UTF-8 is assumed for the locale, andQString's constructors expect UTF-8 bytes by default. For legacy encodings, useQStringConverter/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::Encodingenum lists the built-in encodings: UTF-8, UTF-16, UTF-16LE/BE, UTF-32 variants, Latin-1, System. Anything else needs theQt5Compatmodule (Qt6::Core5Compat::QTextCodec) — Qt 6 keepsQTextCodecavailable as a compatibility shim.
9. References
- Qt 6 — Container Classes — overview, complexity, iteration patterns.
- Qt 6 — Implicit Sharing — COW semantics and detach behavior.
- Qt 6 —
QString— full string API. - Qt 6 — Internationalization with Qt —
tr,lupdate,lrelease, plurals. - Qt 6 —
QStringConverter— the Qt 6 replacement forQTextCodec.