访问 std::variant 的不安全、`noexcept` 和无开销的方式
Unsafe, `noexcept` and no-overhead way of accessing `std::variant`
std::variant
提供以下访问函数:
std::get_if
:取指针到variant
,return指针 替代。
template <std::size_t I, typename... Ts>
auto* std::get_if(std::variant<Ts...>* pv) noexcept;
If pv
is not a null pointer and pv->index() == I
, returns a pointer to the value stored in the variant pointed to by pv
. Otherwise, returns a null pointer value.
这意味着 get_if
的实现大致如下所示:
template <std::size_t I, typename... Ts>
auto* std::get_if(std::variant<Ts...>* pv) noexcept
{
if(pv == nullptr) return nullptr;
if(pv->index() != I) return nullptr;
return &(pv->real_get<I>());
}
std::get
: 取reference to variant
, return reference 替代,throw
无效访问。
template <std::size_t I, typename... Ts>
auto& std::get(std::variant<Ts...>& v);
If v.index() == I
, returns a reference to the value stored in v
. Otherwise, throws std::bad_variant_access
.
这意味着 get
的实现大致如下所示:
template <std::size_t I, typename... Ts>
auto& std::get(std::variant<Ts...>& v)
{
if(v.index() != I) throw std::bad_variant_access{};
return v.real_get<I>();
}
我想要一个不安全的访问函数:
是noexcept
.
对variant
进行引用,避免任何pv == nullptr
检查。
具有 未定义的行为 如果 v.index() != I
.
为什么?因为在某些情况下,我可以 100% 确定特定 variant
实例在代码路径中包含特定类型。此外,在编写已经单独检查 v.index() != I
(例如编写我自己的 visit
).
的通用代码时,它会很有用
示例实现:
template <std::size_t I, typename... Ts>
auto& unsafe_get(std::variant<Ts...>& v)
{
return v.real_get<I>();
}
标准里有这样的吗?没找到。如果没有,是否可以为 std::variant
实施,或者我是否需要推出自己的 variant
实施?
我认为您必须自己实现整个变体。
尽管不受限制的联合会有所帮助 - 它们至少解决了将多个类型放在同一位置并处理对齐问题的问题。
正如@T.C指出的那样。在评论中,您的第一个和第三个 desiderata 是互不相容的。这在 N3279 中有详细说明,标题为 "Conservative use of noexcept in the Library"。
基本上有两种 类 合同:窄合同和宽合同。函数或操作的 wide 契约不指定任何未定义的行为。
这样的合同没有先决条件。在标准库中,只有具有广泛契约的函数才被标记为 noexcept
。
OTOH,narrow 合约是一个不宽的合约。当以违反记录契约的方式调用时,函数或操作的狭窄契约会导致 未定义的行为 。它们不能被标记为 noexcept
。相反,您可以期望的最好结果是它们被记录为 "Throws: Nothing."
看来您运气不好,std::variant
的当前提案中没有提供此类未经检查的访问权限。
std::variant
提供以下访问函数:
std::get_if
:取指针到variant
,return指针 替代。template <std::size_t I, typename... Ts> auto* std::get_if(std::variant<Ts...>* pv) noexcept;
If
pv
is not a null pointer andpv->index() == I
, returns a pointer to the value stored in the variant pointed to bypv
. Otherwise, returns a null pointer value.这意味着
get_if
的实现大致如下所示:template <std::size_t I, typename... Ts> auto* std::get_if(std::variant<Ts...>* pv) noexcept { if(pv == nullptr) return nullptr; if(pv->index() != I) return nullptr; return &(pv->real_get<I>()); }
std::get
: 取reference tovariant
, return reference 替代,throw
无效访问。template <std::size_t I, typename... Ts> auto& std::get(std::variant<Ts...>& v);
If
v.index() == I
, returns a reference to the value stored inv
. Otherwise, throwsstd::bad_variant_access
.这意味着
get
的实现大致如下所示:template <std::size_t I, typename... Ts> auto& std::get(std::variant<Ts...>& v) { if(v.index() != I) throw std::bad_variant_access{}; return v.real_get<I>(); }
我想要一个不安全的访问函数:
是
noexcept
.对
variant
进行引用,避免任何pv == nullptr
检查。具有 未定义的行为 如果
v.index() != I
.
为什么?因为在某些情况下,我可以 100% 确定特定 variant
实例在代码路径中包含特定类型。此外,在编写已经单独检查 v.index() != I
(例如编写我自己的 visit
).
示例实现:
template <std::size_t I, typename... Ts>
auto& unsafe_get(std::variant<Ts...>& v)
{
return v.real_get<I>();
}
标准里有这样的吗?没找到。如果没有,是否可以为 std::variant
实施,或者我是否需要推出自己的 variant
实施?
我认为您必须自己实现整个变体。 尽管不受限制的联合会有所帮助 - 它们至少解决了将多个类型放在同一位置并处理对齐问题的问题。
正如@T.C指出的那样。在评论中,您的第一个和第三个 desiderata 是互不相容的。这在 N3279 中有详细说明,标题为 "Conservative use of noexcept in the Library"。
基本上有两种 类 合同:窄合同和宽合同。函数或操作的 wide 契约不指定任何未定义的行为。
这样的合同没有先决条件。在标准库中,只有具有广泛契约的函数才被标记为 noexcept
。
OTOH,narrow 合约是一个不宽的合约。当以违反记录契约的方式调用时,函数或操作的狭窄契约会导致 未定义的行为 。它们不能被标记为 noexcept
。相反,您可以期望的最好结果是它们被记录为 "Throws: Nothing."
看来您运气不好,std::variant
的当前提案中没有提供此类未经检查的访问权限。