有没有办法从已知替代方法重置std :: variant? [英] Is there a way to reset a std::variant from a known alternative?

查看:128
本文介绍了有没有办法从已知替代方法重置std :: variant?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在更新一个代码库,该代码库当前使用与C ++ 17等效的自定义std::variant.

I'm in the process of updating a codebase that is currently using a custom equivalent of std::variant to C++17 .

在代码的某些部分,该变体正在从已知的替代方法中重置,因此该类提供了一种方法,该方法断言index()为当前值,但仍无条件直接调用适当的析构函数.

In certain parts of the code, the variant is being reset from a known alternative, so the class provides a method that asserts that index() is at a current value, but still directly invokes the proper destructor unconditionally.

这在一些紧密的内部循环中使用,并且具有(可衡量的)对性能的重要影响.这是因为当所讨论的替代项是可微破坏的类型时,它允许编译器消除整个破坏.

This is used in some tight inner loops, and has (measured) non-trivial performance impact. That's because it allows the compiler to eliminate the entire destruction when the alternative in question is a trivially destructible type.

从表面上看,在我看来,使用STL中的当前std::variant<>实现无法实现这一点,但我希望自己是错的.

At face value, it seems to me that I can't achieve this with the current std::variant<> implementation in the STL, but I'm hoping that I'm wrong.

有没有办法实现我所没有看到的,还是我不走运?

Is there a way to accomplish this that I'm not seeing, or am I out of luck?

,根据要求,这是一个用法示例(以@ T.C的示例为基础):

as requested, here's a usage example (using @T.C's example as 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编译为有效:

@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.的各种见解,我进入了这种怪诞状态.即使它确实通过跳过一些析构函数而违反了标准,也可以运行".但是,在编译时会检查每个跳过的析构函数都是微不足道的,因此...:

Thanks to various insight from @T.C., I've landed on this monstrosity. It "works" even though it does violate the standard by skipping a few destructors. However, every skipped destructor is checked at compile-time to be trivial so...:

查看Godbolt: https://godbolt.org/g/2LK2fa

see on 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的代码源OTOH仍然有很多不足之处,尽管它还不错(如果您使用(相当于断言),而不是__builtin_unreachable():

which is optimal. Clang's codegen, OTOH, leaves much to be desired, although it isn't too bad if you use std::terminate() (the equivalent of an assertion) rather than __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.

MSVC...let's not talk about MSVC.

这篇关于有没有办法从已知替代方法重置std :: variant?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆