为什么我在使用std::variant时会收到std::bad_variant_access错误?

3

考虑:

#include <variant>
#include <iostream>

int main()
{
    double foo = 666.666;
    std::variant<double, uint64_t> v = foo;
    std::cout << std::get<uint64_t>(v) << std::endl;
    return 0;
}

这会导致:
terminate called after throwing an instance of 'std::bad_variant_access'
  what():  Unexpected index
Aborted (core dumped)

为什么?

我理解 std::variant 应该是 union 的替代品。但如果这种基础的东西都失败了,那还有什么用处呢?


7
union 不适合用于类型转换。如果读取不同于上次写入的 union 成员,则这种行为总是未定义的行为。 - Yksisarvinen
5
你的 variant 中没有 uint64_t,你为什么期望它能给你一个?我会翻译成:在 variant 中没有 uint64_t 类型,你为何期待它会给你一个? - NathanOliver
2
你将其分配为双精度浮点数,但读取时却作为整数。你期望什么?使用联合来进行这样的操作会进入未定义行为领域,因此抛出异常要好得多。 - Timo
6
如果您想这样做,那么用C++实现的方法是使用memcpy将double类型转换成uint64_t类型。 - NathanOliver
3
“但是如果这种基础的东西都失败了,它还有什么用处呢?”我认为这是基础知识成功的一个很好的例子 :-) - Barry
显示剩余8条评论
2个回答

7
我理解std::variant是union的替代品。但是你对C++中的union存在误解。来自cppreference
读取最近未被写入的联合成员是未定义行为。许多编译器实现了一种非标准语言扩展,能够读取不活动的联合成员。
请注意,这与C不同,这是常见误解的一个根本原因,即访问非活动成员在C++中也是允许的。
为什么?
因为std::variant模拟了联合,但增加了一些安全性,std::get是...
基于索引的值访问器:如果v.index() == I,则返回对存储在v中的值的引用。否则,抛出std::bad_variant_access异常。如果I不是变量中的有效索引,则调用无效。

.

但如果这种基础性的东西都失败了,那还有什么用呢?在C++中,联合体从来没有被设计成像这样使用。联合体主要是为了节省内存,当你想把两个或更多的值挤进同一块内存时使用(但任何时候只使用其中一个)。我从未遇到过真正的用例。然而,std::variant是C++类型的一个不错的补充。更大的图景是,早期已经有所谓的乘积类型,比如std::pair,它们能够表示的值的集合是T1 x T2。现在,通过std::variant(和std::any),C++也拥有了适当的(带有一定程度的类型安全性,这是联合体无法提供的)和类型的总和类型,即std::variant可以表示的值的集合是T1 + T2(对于使用松散符号的表述,抱歉,希望它很清晰)。

1

std::variant是一种“类型安全的联合体”,这意味着它将检查您从中获取的是最后存储在其中的类型。在这个例子中,您正在存储一个double,但尝试获取一个uint64_t

请参阅此处的文档: https://en.cppreference.com/w/cpp/utility/variant/get

基于类型的值访问器:如果v持有T的替代品,则返回对存储在v中的值的引用。否则,抛出std :: bad_variant_access。如果T不是Types的唯一元素,则调用无效....


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接