c++ - Is there a way to reset a std::variant from a known alternative? -
i'm in process of updating codebase using custom equivalent of std::variant c++17 .
in parts of code, variant being reset known alternative, class provides method asserts index() @ current value, still directly invokes proper destructor unconditionally.
this used in tight inner loops, , has (measured) non-trivial performance impact. that's because allows compiler eliminate entire destruction when alternative in question trivially destructible type.
at face value, seems me can't achieve current std::variant<> implementation in stl, i'm hoping i'm wrong.
is there way accomplish i'm not seeing, or out of luck?
edit: requested, here's usage example (using @t.c's example basis):
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 compiles effectively:
@change_int_to_double(myvariant<s, int, double>&) mov qword ptr [rdi], 0 // sets storage double(0.0) mov dword ptr [rdi + 8], 2 // sets index 2 edit #2
thanks various insight @t.c., i've landed on monstrosity. "works" though violate standard skipping few destructors. however, every skipped destructor checked @ compile-time trivial so...:
see on godbolt: https://godbolt.org/g/2lk2fa
// let's make sure our std::variant implementation nothing funky internally. static_assert(std::is_trivially_destructible<std::variant<char, int>>::value, "change_from_i won't 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 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 fine without check, msvc can // use 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 compiles function down to:
change_int_to_double(std::variant<s, int, double>&): mov qword ptr [rdi], 0x000000000 mov byte ptr [rdi+8], 2 ret which optimal. clang's codegen, otoh, leaves desired, although isn't bad if use std::terminate() (the equivalent of assertion) rather __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...let's not talk msvc.
Comments
Post a Comment