[英]Is there a way to reset a std::variant from a known alternative?
我正在更新當前使用自定義等效的std::variant
到C ++ 17的代碼庫。
在代碼的某些部分中,變量從一個已知的替代方法重置,因此該類提供了一個方法,斷言index()
是當前值,但仍然無條件地直接調用正確的析構函數。
這用於一些緊密的內環,並且具有(測量的)非平凡的性能影響。 這是因為當有問題的替代方案是一個簡單的可破壞類型時,它允許編譯器消除整個破壞。
從表面上看,在我看來,我無法通過STL中當前的std::variant<>
實現來實現這一點,但我希望我錯了。
有沒有辦法實現這一點,我沒有看到,或者我運氣不好?
編輯:根據要求,這是一個用法示例(使用@ TC的示例作為基礎):
struct S {
~S();
};
using var = MyVariant<S, int, double>;
void change_int_to_double(var& v){
v.reset_from<1>(0.0);
}
change_int_to_double
有效編譯:
@change_int_to_double(MyVariant<S, int, double>&)
mov qword ptr [rdi], 0 // Sets the storage to double(0.0)
mov dword ptr [rdi + 8], 2 // Sets the index to 2
編輯#2
感謝來自@TC的各種見解,我已經登上了這個怪物。 即使它跳過一些析構函數確實違反了標准,它也“有效”。 但是,在編譯時檢查每個跳過的析構函數都是微不足道的,所以...:
請參閱godbolt: https ://godbolt.org/g/2LK2fa
// Let's make sure our std::variant implementation does nothing funky internally.
static_assert(std::is_trivially_destructible<std::variant<char, int>>::value,
"change_from_I won't be valid");
template<size_t I, typename arg_t, typename... VAR_ARGS>
void change_from_I(std::variant<VAR_ARGS...>& v, arg_t&& new_val) {
assert(I == v.index());
// Optimize away the std::get<> runtime check if possible.
#if defined(__GNUC__)
if(v.index() != I) __builtin_unreachable();
#else
if(v.index() != I) std::terminate();
#endif
// Smart compilers handle this fine without this check, but MSVC can
// use the help.
using current_t = std::variant_alternative_t<I, std::variant<VAR_ARGS...>>;
if(!std::is_trivially_destructible<current_t>::value) {
std::get<I>(v).~current_t();
}
new (&v) var(std::forward<arg_t>(new_val));
}
#include <variant>
struct S {
~S();
};
using var = std::variant<S, int, double>;
void change_int_to_double(var& v){
if(v.index() != 1) __builtin_unreachable();
v = 0.0;
}
GCC 將函數編譯為 :
change_int_to_double(std::variant<S, int, double>&):
mov QWORD PTR [rdi], 0x000000000
mov BYTE PTR [rdi+8], 2
ret
這是最佳的。 Clang的codegen,OTOH,還有很多不足之處,盡管如果你使用std::terminate()
(相當於一個斷言)而不是__builtin_unreachable()
並不是太糟糕了 :
change_int_to_double(std::__1::variant<S, int, double>&): # @change_int_to_double(std::__1::variant<S, int, double>&)
cmp dword ptr [rdi + 8], 1
jne .LBB0_2
mov qword ptr [rdi], 0
mov dword ptr [rdi + 8], 2
ret
.LBB0_2:
push rax
call std::terminate()
MSVC ......我們不要談論MSVC。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.