簡體   English   中英

有沒有辦法從已知的替代方案重置std :: variant?

[英]Is there a way to reset a std::variant from a known alternative?

我正在更新當前使用自定義等效的std::variant到C ++ 17的代碼庫。

在代碼的某些部分中,變量從一個已知的替代方法重置,因此該類提供了一個方法,斷言index()是當前值,但仍然無條件地直接調用正確的析構函數。

這用於一些緊密的內環,並且具有(測量的)非平凡的性能影響。 這是因為當有問題的替代方案是一個簡單的可破壞類型時,它允許編譯器消除整個破壞。

從表面上看,在我看來,我無法通過STL中當前的std::variant<>實現來實現這一點,但我希望我錯了。

有沒有辦法實現這一點,我沒有看到,或者我運氣不好?

編輯:根據要求,這是一個用法示例(使用@ TC的示例作為基礎):

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

感謝來自@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));
}
#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的codegen,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()

MSVC ......我們不要談論MSVC。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM