std::monostate 是专为 std::variant 设计的空类型,大小为1字节,使 variant 能默认构造;它不携带数据、不可赋值、所有实例相等,仅作合法默认状态占位符,需置于模板参数首位以确保 index() 为0。

std::monostate 是什么,为什么 variant 需要它
std::monostate 是一个空的、无状态的类型,专为 std::variant 设计。它本身不携带任何数据,大小为 1 字节(满足 trivial 类型要求),且默认可构造、可复制、可比较。它的唯一作用是:让 std::variant 在没有显式指定初始值时,能有一个合法的默认构造状态。
比如 std::variant<int std::string></int> 无法默认构造——编译器不知道该初始化成 int{} 还是 std::string{}。但加上 std::monostate:std::variant<:monostate int std::string></:monostate>,就天然支持 variant<...> v;</...>,此时 v.index() 为 0,即持有 std::monostate。
怎么用 std::monostate 让 variant 默认构造
把 std::monostate 放在 std::variant 模板参数列表最前面(推荐),然后直接定义变量:
std::variant<std::monostate, int, std::string> v; // ✅ 默认构造成功
常见错误写法:
立即学习“C++免费学习笔记(深入)”;
- 漏掉
std::monostate,直接写std::variant<int std::string> v;</int>→ 编译失败,报错类似error: call to implicitly-deleted default constructor - 把
std::monostate放在中间或末尾,比如std::variant<int std::monostate std::string></int>→ 虽然语法合法,但v.index()默认是 1(对应std::monostate),容易让人误以为“第二个类型才是默认”,可读性差,不推荐 - 试图用
std::in_place_index显式构造却忘了加std::monostate→ 依然编译不过,因为模板参数里根本没它
std::monostate 的行为和注意事项
std::monostate 不可赋值、不可比较(除 == 和 != 外)、不参与 std::get 提取(std::get<:monostate>(v)</:monostate> 合法但返回临时对象,几乎无用)。它只是一个“占位符”。
关键点:
- 它不增加
std::variant的内存开销(sizeof(std::monostate) == 1,而variant的 size 已由最大成员+对齐决定) - 所有
std::monostate实例都相等:std::monostate{} == std::monostate{}返回true - 不能对它调用
std::get_if<:monostate>(&v)</:monostate>来判断是否为空——应该用v.index() == 0或封装一个is_empty()辅助函数 - 它不是“空值”语义的替代品(比如不像
std::optional那样表示“未设置”),只是让variant能默认构造的机制手段
替代方案对比:optional vs monostate
有人会想:既然要“可空”,为什么不直接用 std::optional<:variant std::string>></:variant>?
区别很实际:
-
std::optional<variant>></variant>多一层嵌套,sizeof至少多 1 字节(存储has_value),访问需两层解包:opt->index() -
variant<monostate ...></monostate>更扁平,内存布局更紧凑,且index()直接反映当前状态,适合需要频繁判别类型的场景 - 但
std::monostate无法表达“从有值变回无值”的意图(比如你希望清空一个已含int的 variant);这时得手动赋值:v = std::monostate{};
真正容易被忽略的是:一旦用了 std::monostate,所有 std::visit 的 visitor 都必须处理这个分支——哪怕你只把它当默认构造占位符,漏掉 [](const std::monostate&) {} 就会导致编译失败。









