Fra*_*ank 14 c++ variant c++17
我正在更新当前使用自定义等效std::variant于C++ 17 的代码库.
在代码的某些部分中,变量是从已知的替代方法重置的,因此该类提供了一个断言index()当前值的方法,但仍然无条件地直接调用正确的析构函数.
这用于一些紧密的内环,并且具有(测量的)非平凡的性能影响.这是因为当有问题的替代方案是一个简单的可破坏类型时,它允许编译器消除整个破坏.
从表面上看,在我看来,我无法std::variant<>通过STL中的当前实现实现这一点,但我希望我错了.
有没有办法实现这一点,我没有看到,或者我运气不好?
编辑:根据要求,这是一个用法示例(使用@ TC的示例作为基础):
struct S {
~S();
};
using var = MyVariant<S, int, double>;
void change_int_to_double(var& v){
v.reset_from<1>(0.0);
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
编辑#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));
}
Run Code Online (Sandbox Code Playgroud)
#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;
}
Run Code Online (Sandbox Code Playgroud)
GCC 将函数编译为:
change_int_to_double(std::variant<S, int, double>&):
mov QWORD PTR [rdi], 0x000000000
mov BYTE PTR [rdi+8], 2
ret
Run Code Online (Sandbox Code Playgroud)
这是最佳的.铛的代码生成,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()
Run Code Online (Sandbox Code Playgroud)
MSVC ......我们不要谈论MSVC.