获取联合的第 n 个元素的安全方法
safe way to get the n-th element of a union
我正在写一个变体 class(是的,我知道 std::variant,只是为了好玩),这就是我目前所拥有的
template<typename First, typename... Rest>
union Variant<First, Rest...>
{
template<size_t N>
using Types = typename Type<N, First, Rest...>::_Type;
First first;
Variant<Rest...> rest;
uint16_t activeIndex;
template<size_t N>
Types<N>& get()
{
}
};
Type 结构只允许我找到第 n 个元素的类型。
我知道联合会的所有成员都在同一个内存地址,所以我可以
return *(Types<N>*)&first
在获取函数中?这安全吗?
或者还有其他方法可以做到这一点。
任何帮助将不胜感激。
您本质上想知道的是操作是否:
*(Types<N>*)&first
遵守strict aliasing. The answer is yes, so long as the type returned by Types<N>
is a compatible type:
The types T and U are compatible, if they are the same type (same name or aliases introduced by a typedef)
在您的示例中,我猜测这是通过说必须使用相同类型设置和检索值来以某种方式传递给用户的。如果是这种情况并且用户遵守此规则,那么它是安全的。
如果你想更上一层楼,可以看看std::variant
的实现。如果您尝试检索未正确设置的值,std::variant::get
函数实际上 会抛出 。要做到这一点,你可以想到使用Types<N>
的反向来获取索引,并在设置时存储它。然后检查这个是不是你在get
时使用的索引,如果不一样就抛出。
与直接使用地址类型转换相比,另一种方法是使用递归。
为了减少递归的深度,可以使用多个if constexpr
分支提前停止递归。
template<size_t N>
Types<N>& get() const {
if constexpr (N == 0)
return first;
else if constexpr (N == 1)
return rest.first;
else if constexpr (N == 2)
return rest.rest.first;
else
return rest.rest.rest.template get<N - 3>();
}
我正在写一个变体 class(是的,我知道 std::variant,只是为了好玩),这就是我目前所拥有的
template<typename First, typename... Rest>
union Variant<First, Rest...>
{
template<size_t N>
using Types = typename Type<N, First, Rest...>::_Type;
First first;
Variant<Rest...> rest;
uint16_t activeIndex;
template<size_t N>
Types<N>& get()
{
}
};
Type 结构只允许我找到第 n 个元素的类型。 我知道联合会的所有成员都在同一个内存地址,所以我可以
return *(Types<N>*)&first
在获取函数中?这安全吗? 或者还有其他方法可以做到这一点。 任何帮助将不胜感激。
您本质上想知道的是操作是否:
*(Types<N>*)&first
遵守strict aliasing. The answer is yes, so long as the type returned by Types<N>
is a compatible type:
The types T and U are compatible, if they are the same type (same name or aliases introduced by a typedef)
在您的示例中,我猜测这是通过说必须使用相同类型设置和检索值来以某种方式传递给用户的。如果是这种情况并且用户遵守此规则,那么它是安全的。
如果你想更上一层楼,可以看看std::variant
的实现。如果您尝试检索未正确设置的值,std::variant::get
函数实际上 会抛出 。要做到这一点,你可以想到使用Types<N>
的反向来获取索引,并在设置时存储它。然后检查这个是不是你在get
时使用的索引,如果不一样就抛出。
与直接使用地址类型转换相比,另一种方法是使用递归。
为了减少递归的深度,可以使用多个if constexpr
分支提前停止递归。
template<size_t N>
Types<N>& get() const {
if constexpr (N == 0)
return first;
else if constexpr (N == 1)
return rest.first;
else if constexpr (N == 2)
return rest.rest.first;
else
return rest.rest.rest.template get<N - 3>();
}