我正在更新当前使用自定义等效项的代码库std::variant
到 C++17 。
在代码的某些部分,变体是从已知的替代方案中重置的,因此该类提供了一个方法来断言index()
处于当前值,但仍然无条件地直接调用适当的析构函数。
这在一些紧密的内部循环中使用,并且具有(测量的)不小的性能影响。这是因为,当所讨论的替代方案是普通可破坏类型时,它允许编译器消除整个破坏。
从表面上看,在我看来,以目前的水平我无法实现这一目标std::variant<>
STL 中的实现,但我希望我是错的。
有没有一种方法可以实现我没有看到的功能,或者我运气不好?
Edit:根据要求,这是一个使用示例(使用@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
Edit #2
感谢@T.C. 的各种见解,我终于发现了这个怪物。尽管它跳过一些析构函数确实违反了标准,但它仍然“有效”。然而,每个跳过的析构函数在编译时都会被检查为微不足道,所以......:
参见 Godbolt:https://godbolt.org/g/2LK2fa 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));
}