QVariant 保存一个带运行时类型信息的值,常用于 Qt 模型、属性和通用数据接口。它不是同时装下很多值的容器;再次赋值会改变当前保存的内容。std::variant 在编译期列出候选类型,QVariant 与 Qt 元类型和转换机制结合,二者适用场景不同,不宜笼统评价谁“更强大”。
QVariantMap 则是 QMap<QString, QVariant> 的别名,用字符串键组合多个 QVariant 值。它适合较灵活的配置或界面数据;字段固定、约束重要的业务数据,普通结构体往往更容易检查。
类型可转换,不代表这次转换成功
跳转到“类型可转换,不代表这次转换成功”| 操作 | 应检查什么 |
|---|---|
isValid() | 是否保存了有效的类型;不表示内容符合业务规则 |
metaType()、typeId() | Qt 6 中查看实际保存的类型;旧 type() 不作为新示例接口 |
canConvert<T>() | 是否存在到目标类型的转换路径;不能保证这个具体值可转换 |
toInt(&ok)、toDouble(&ok) | 用 ok 区分转换失败和合法的零;还要另行检查范围或单位 |
toString()、toList()、toMap() | 需要明确预期来源类型;空结果不自动意味着原值就是空值 |
value<T>() | 按指定类型取值;不要把失败后的默认值当成已成功解析的数据 |
isNull() | 与无效值、空字符串和业务上的“缺失”分别处理;Qt 6 的空值规则不能照搬 Qt 5 |
例如,字符串类型有到整数的转换路径,但 "12abc" 转为整数仍会失败。原示例中的 123、"Hello, QVariant!"、3.14 都保留在下面的可运行程序中。Qt:QVariant
Map 读取、插入和顺序
跳转到“Map 读取、插入和顺序”insert(key, value) 添加或覆盖值。value(key) 在缺键时返回默认 QVariant,且不插入;非 const 的 operator[](key) 会创建缺失键。若要区分“缺少字段”和“字段存在但为空”,先用 contains() 或 constFind() 检查。
QVariantMap 按键排序,keys() 与 values() 遵循相同的键顺序,并不记录插入先后。需要稳定的业务展示顺序时,可单独定义字段顺序,或使用明确的列表结构。Qt:QMap
完整示例:转换、配置读取与 JSON
跳转到“完整示例:转换、配置读取与 JSON”#include <QByteArray>#include <QJsonDocument>#include <QJsonParseError>#include <QMetaType>#include <QObject>#include <QString>#include <QStringList>#include <QVariant>#include <QVariantMap>#include <cassert>
int main() { const QVariant integer(123); const QVariant text(QStringLiteral("Hello, QVariant!")); const QVariant real(3.14); assert(integer.metaType() == QMetaType::fromType<int>()); assert(text.typeId() == QMetaType::QString); bool ok = false; assert(integer.toInt(&ok) == 123 && ok); assert(text.toString() == QStringLiteral("Hello, QVariant!")); assert(real.toDouble(&ok) == 3.14 && ok);
const QVariant invalid; const QVariant emptyText(QStringLiteral("")); const QVariant nullObject = QVariant::fromValue(static_cast<QObject *>(nullptr)); assert(!invalid.isValid()); assert(emptyText.isValid() && emptyText.toString().isEmpty()); assert(nullObject.isValid() && nullObject.isNull()); const QVariant badInteger(QStringLiteral("12abc")); assert(badInteger.canConvert<int>()); const int failedValue = badInteger.toInt(&ok); assert(!ok && failedValue == 0); const int validZero = QVariant(QStringLiteral("0")).toInt(&ok); assert(ok && validZero == 0);
QVariantMap map; map.insert(QStringLiteral("name"), QStringLiteral("Kimi")); map.insert(QStringLiteral("age"), 25); map.insert(QStringLiteral("isDeveloper"), true); assert(map.value(QStringLiteral("name")).toString() == QStringLiteral("Kimi")); const int age = map.value(QStringLiteral("age")).toInt(&ok); assert(ok && age == 25); assert(map.value(QStringLiteral("isDeveloper")).toBool()); const QStringList keys = map.keys(); assert((keys == QStringList{"age", "isDeveloper", "name"})); const QVariantList values = map.values(); assert(values.size() == keys.size()); for (qsizetype i = 0; i < keys.size(); ++i) assert(values.at(i) == map.value(keys.at(i)));
const auto originalSize = map.size(); assert(!map.value(QStringLiteral("nickname")).isValid()); assert(map.size() == originalSize); QVariant &created = map[QStringLiteral("nickname")]; assert(!created.isValid() && map.contains(QStringLiteral("nickname"))); assert(map.size() == originalSize + 1); map.remove(QStringLiteral("nickname"));
QObject settings; settings.setProperty("profile", map); // 动态属性;用读取结果验证存储 assert(settings.property("profile").toMap().value("name").toString() == "Kimi");
const QByteArray json = QJsonDocument::fromVariant(map).toJson(QJsonDocument::Compact); QJsonParseError parseError; const QJsonDocument document = QJsonDocument::fromJson(json, &parseError); assert(parseError.error == QJsonParseError::NoError && document.isObject()); const QVariantMap restored = document.toVariant().toMap(); assert(restored.value("name").toString() == "Kimi"); assert(restored.value("age").toInt(&ok) == 25 && ok); assert(restored.value("isDeveloper").toBool());}保存为 main.cpp,在 Debug 构建中运行:
cmake_minimum_required(VERSION 3.20)project(VariantExample LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(Qt6 REQUIRED COMPONENTS Core)add_executable(variant_example main.cpp)target_link_libraries(variant_example PRIVATE Qt6::Core)动态属性和预先声明的 Q_PROPERTY 不同。给 QObject 新增动态属性时,setProperty() 的返回值不能简单当作“动态属性未写入”的证明;本例读取属性核对结果。Qt:QObject::setProperty
JSON 往返只验证这里使用的字符串、整数和布尔字段。JSON 不是任意 QVariant 的无损序列化格式:自定义结构、对象指针以及精确的原始 C++ 类型身份不能靠它自动保留。文件或网络数据还应校验字段是否存在、类型、数值范围及长度;不能只调用 toInt() 就认为配置有效。Qt:QJsonDocument
自定义值类型的声明、运行时注册及排队传递,见 [[10-knowledge/computing/software-development/application-development/desktop/qt/core/metatypes-and-queued-values|元类型与排队传值]]。