std::monostate 是 std::variant 默认构造所需的空状态类型,大小为0、平凡可构造/析构;不能用 void 或 std::nullopt_t 替代,因其非合法类型或非独立类型;使用时须结合 valueless_by_exception() 判断真实状态。

std::monostate 是 variant 的默认构造占位符
当 std::variant 没有显式初始化任何备选项时,它必须有一个合法的初始状态;std::monostate 就是为此设计的空状态类型——它不携带任何数据、大小为 0(通常被优化掉),且满足 std::is_default_constructible_v 和 std::is_trivially_destructible_v。没有它,std::variant<int std::string></int> 这样的类型就无法默认构造。
为什么不能直接用 std::nullopt 或 void?
std::nullopt_t 不是独立类型,它只是 std::optional 的标签,不能作为 variant 的合法备选项(编译失败);void 更不行——它不是可实例化的类型,连 sizeof 都非法。而 std::monostate 是标准库明确定义的、唯一用途就是“表示无值”的平凡类型。
- 错误写法:
std::variant<int void></int>→ 编译报错:error: 'void' is not a valid type for a variant alternative - 错误写法:
std::variant<int std::nullopt_t></int>→ 可能通过,但语义混乱,且无法与std::optional机制互通 - 正确写法:
std::variant<:monostate int std::string></:monostate>→ 默认构造后index() == 0,明确表示“尚未赋值”
实际使用中要注意 index() 和 valueless_by_exception()
std::monostate 占位后,index() 返回 0 并不总代表“用户有意设为空”,也可能是异常中途抛出导致 variant 进入 valueless_by_exception 状态——此时 index() 仍可能返回 0,但访问会触发 std::bad_variant_access。
- 安全判断空状态:先检查
v.valueless_by_exception(),再看v.index() == 0是否对应std::monostate - 避免误判:仅靠
v.index() == 0不能断定是“逻辑空”,必须确认该位置确实是std::monostate - 示例:
std::variant<:monostate int> v; assert(v.index() == 0);</:monostate>—— 此时是合法空态;但若构造时抛异常,v.index()可能仍是 0,却不可访问
替代方案:是否真需要 std::monostate?
多数场景下,std::monostate 是最轻量、最标准的“空占位符”,但并非唯一选择:
立即学习“C++免费学习笔记(深入)”;
- 如果想表达“未初始化/无效值”,且已有自然的哨兵值(如
int用-1),可省略std::monostate,靠业务逻辑判断 - 若需区分“未设置”和“设为默认值”,
std::optional<variant>></variant>更清晰,但多一层间接和内存开销 -
std::monostate不支持比较或输出,打印调试时需手动处理;若频繁需要日志,建议封装访问逻辑
真正容易被忽略的是:它只解决“默认构造合法性”问题,不提供语义层面的“空值协议”——业务含义仍需开发者自己定义和维护。










