Я в процессе обновления базы кода, которая в настоящее время использует пользовательский эквивалент std::variant
для С++ 17.
В некоторых частях кода вариант является reset из известной альтернативы, поэтому класс предоставляет метод, который утверждает, что index()
находится в текущем значении, но все же непосредственно вызывает надлежащий деструктор безоговорочно.
Это используется в некоторых жестких внутренних петлях и имеет (измеренное) нетривиальное влияние производительности. Это потому, что это позволяет компилятору устранить все разрушения, когда рассматриваемая альтернатива является тривиально разрушаемым типом.
По значению лица мне кажется, что я не могу добиться этого с помощью текущей реализации std::variant<>
в STL, но я надеюсь, что я ошибаюсь.
Есть ли способ выполнить это, чего я не вижу, или мне не повезло?
Изменить: в соответствии с запросом, здесь пример использования (используя пример @T.C в качестве основы):
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
Благодаря различной проницательности от @T.C., я приземлился на это чудовище. Он "работает", даже если он нарушает стандарт, пропуская несколько деструкторов. Тем не менее, каждый пропущенный деструктор проверяется во время компиляции, чтобы быть тривиальным, поэтому...:
см. на godbolt: https://godbolt.org/g/2LK2fa
// Let 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));
}