2017-09-11 34 views
13

我正在更新一個代碼庫,目前使用的定製等效物爲std::variant C++ 17。有沒有辦法從一個已知的選擇重置std :: variant?

在代碼的某些部分,該變體正在從已知的替代方法中重置,因此該類提供了一種方法,該方法聲明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的各種見解,我已登陸這個怪物trosity。它「起作用」,即使它通過跳過一些析構函數而違反了標準。然而,每一個跳過析構函數在編譯時被檢查爲瑣碎所以...:

看到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)); 
} 
+0

偶然是所有類型的變體trivially可破壞? – vu1p3n0x

+0

@ vu1p3n0x它有點兒在模板層次結構深處鬆動,所以答案是:有時是,有時不是。我還沒有測試過編譯器是否處理yes情況,但沒關係,因爲我希望它在混合時工作。 – Frank

+0

「*該變體正在從一個已知的替代重置*」這是什麼意思,確切地說?你能否使用你的舊型號提供代碼來證明這一點? –

回答

7
#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 compiles the function down to

change_int_to_double(std::variant<S, int, double>&): 
    mov QWORD PTR [rdi], 0x000000000 
    mov BYTE PTR [rdi+8], 2 
    ret 

這是最佳。鐺的代碼生成,OTOH,留下許多不足之處,但它isn't too bad如果你使用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。

+1

作爲參考,是的,當使用std :: terminate方法時,MSVC(2017)實際上保持對'std :: _ Variant_base :: _ Reset'的調用,即使使用最積極的優化選項。這是...不是很好... – Frank

+0

我upvoted的答案,但不會接受它。我寧願有一個解決方案在代碼中執行此操作(就像我曾經擁有過的那樣),而不是希望編譯器爲我優化它。我明白,雖然現在可能不可能,但... – Frank

+0

@Frank嗯,如果你想這樣做的話,總是會有一些瑣碎的破壞和放置新東西的方法...... –

相關問題