[英]How does std::visit handle multiple variants?
與std::visit 如何與 std::variant 一起工作的問題相關,但不是同一個問題?
為單個變體實現std::visit
在概念上如下所示(在 C++ 偽代碼中):
template<class F, class... Ts>
void visit(F&& f, variant<Ts...>&& var) {
using caller_type = void(*)(void*);
caller_type dispatch[] = {dispatch_visitor(f, (INDEX_SEQUENCE))...};
dispatch[var.index()](var);
};
基本上,我們設置了一個跳轉表,為我們的訪問者調用正確的調度程序。
對於多個變體,它似乎更復雜,因為對於這種方法,您需要計算所有變體備選方案的笛卡爾積,並可能模板數千個函數。 這是它的完成方式還是標准庫實現它的更好方式?
對於多個變體,它似乎更復雜,因為對於這種方法,您需要計算所有變體備選方案的笛卡爾積,並可能模板數千個函數。
目前有兩種多變量訪問的實現。
GCC在編譯時根據變體替代類型的個數構建多維function表,並在運行時通過多個索引訪問對應的訪問function。
// Use a jump table for the general case.
constexpr auto& __vtable = __detail::__variant::__gen_vtable<
_Result_type, _Visitor&&, _Variants&&...>::_S_vtable;
auto __func_ptr = __vtable._M_access(__variants.index()...);
return (*__func_ptr)(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__variants)...);
MSVC 使用遞歸 switch-case來執行多變量訪問:
#define _STL_VISIT_STAMP(stamper, n) \
constexpr size_t _Size = _Variant_total_states<_Remove_cvref_t<_Variants>...>; \
static_assert(_Size > (n) / 4 && _Size <= (n)); \
switch (_Idx) { \
stamper(0, _STL_CASE); \
default: \
_STL_UNREACHABLE; \
}
這種 switch-case base 實現在變體數量較少或備選方案數量較少時具有更好的性能,這也使得 GCC 在其最近的補丁中使用這種方法處理簡單的情況,以減少 function 表的實例化。
switch (__v0.index())
{
_GLIBCXX_VISIT_CASE(0)
_GLIBCXX_VISIT_CASE(1)
_GLIBCXX_VISIT_CASE(2)
_GLIBCXX_VISIT_CASE(3)
_GLIBCXX_VISIT_CASE(4)
case variant_npos:
// ...
}
更多實現細節可以參考以下兩篇 Michael Park 的博客:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.