2.19. 变体

变体是无名类型,它们支持可以是许多命名 case 之一的值,每个 case 可能具有不同的值和类型:

var t : variant<i_value:uint,f_value:float>

有一个简写类型 alias 语法来定义 variant:

variant U_F {
    i_value : uint
    f_value : float
}

typedef U_F = variant<i_value:uint,f_value:float> // 与上述声明完全相同

如果任意两个变体具有相同类型的相同命名个案,则它们属于同一类型,并且顺序相同。

变体保存当前 case 的 index ,以及仅当前 case 的值。

当前的大小写选择可以通过 is 运算符检查,并通过 as 运算符访问:

assert(t is i_value)
assert(t as i_value == 0x3f800000)

可以通过复制不同 case 的正确构造的变体来修改整个变体选择:

t = U_F(i_value = 0x40000000)    // 现在是 i_value
t = U_F(f_value = 1.0)           // 现在是 f_value

访问类型不正确的 variant case 将导致 panic:

t = U_F(i_value = 0x40000000)
return t as f_value                 // panic, 无效的变体索引

可通过``?as``作进行安全导航:

return t ?as f_value ?? 1.0         // 如果 t 不f_value,将返回 1.0

也可以以不安全的方式访问 Cases,而无需检查类型:

unsafe {
    t.i_value = 0x3f800000
    return t.f_value                    // 将返回 f_value 占用的内存 -  1.0f
}

当前索引可以通过 variant_index 函数来确定:

var t : U_F
assert(variant_index(t)==0)

特定情况的索引值可以通过 variant_indexsafe_variant_index 类型特征来确定。 对于无效的索引和类型,safe_variant_index 将返回 -1,而 variant_index 将报告编译错误:

assert(typeinfo(variant_index<i_value> t)==0)
assert(typeinfo(variant_index<f_value> t)==1)
assert(typeinfo(variant_index<unknown_value> t)==-1) // compilation error

assert(typeinfo(safe_variant_index<i_value> t)==0)
assert(typeinfo(safe_variant_index<f_value> t)==1)
assert(typeinfo(safe_variant_index<unknown_value> t)==-1)

当前 case 选择可以通过不安全作 safe_variant_index 来修改:

unsafe
    set_variant_index(t, typeinfo variant_index<f_value>(t))

2.19.1. 对齐和数据布局

变体包含当前 case 的“索引”,后跟各个 case 的并集,类似于以下 C++ 布局:

struct MyVariantName {
    int32_t __variant_index;
    union {
        type0   case0;
        type1   case1;
        ...
    };
};

单个个案从相同的偏移量开始。

variant 类型通过其最大大小写的对齐方式进行对齐,但不小于 int32 的对齐方式。