2016-12-14 35 views
1

我正在嘗試使用std :: variant。我將一個std :: variant存儲爲一個類的成員。在下面的代碼中,如果變體是按值存儲的,但事情工作正常,但如果通過引用存儲變體,則不起作用(對於向量情況以及對於自定義對象)。這是爲什麼?通過引用存儲的C++變體類成員

#include <variant> 
#include <vector> 
#include <iostream> 


template<typename T> 
using VectorOrSimple = std::variant<T, std::vector<T>>; 


struct Print { 
void operator()(int v) { std::cout << "type = int, value = " << v << "\n"; } 
void operator()(std::vector<int> v) const { std::cout << "type = vector<int>, size = " << v.size() << "\n"; } 
}; 


class A { 
public: 
    explicit A(const VectorOrSimple<int>& arg) : member(arg) { 
     print(); 
    } 

    inline void print() const { 
     visit(Print{}, member); 
    } 

private: 
    const VectorOrSimple<int> member; // const VectorOrSimple<int>& member; => does not work 
}; 


int main() { 
    int simple = 1; 
    A a1(simple); 
    a1.print(); 

    std::vector<int> vector(3, 1); 
    A a2(vector); 
    a2.print(); 
} 

與錯誤崩潰版本見http://melpon.org/wandbox/permlink/vhnkAnZhqgoYxU1H一個工作版本,並http://melpon.org/wandbox/permlink/T5RCx0ImTLi4gk5e:「終止拋‘的std :: bad_variant_access’ 什麼()的一個實例後調用:意外指數」

奇怪當使用成員存儲的參考編寫boost :: variant版本的代碼時,它會按預期工作(打印向量大小= 3兩次)與gcc7.0(請參閱http://melpon.org/wandbox/permlink/eW3Bs1InG383vp6M)並且不起作用(打印向量大小= 3在構造函數中,然後在隨後的print()調用中向量大小= 0,但沒有崩潰)與叮噹聲4.0(請參見http://melpon.org/wandbox/permlink/2GRf2y8RproD7XDM)。

這很混亂。有人可以解釋發生了什麼嗎? 謝謝。

回答

5

它不起作用,因爲此語句A a1(simple);創建了一個臨時變體對象!

然後,您繼續將所述臨時綁定到您的const引用。但是在a1的建設結束後,臨時超出範圍,給你一個懸而未決的參考。很明顯,創建副本很有效,因爲它始終涉及使用有效副本。

一個可能的解決方案(如果老是複製擔憂的表現你)是接受按值變量對象,然後將其移動到你的本地副本,就像這樣:

explicit A(VectorOrSimple<int> arg) : member(std::move(arg)) { 
    print(); 
} 

這將使你的用左值或右值調用構造函數。對於左值,您的member將通過移動源變體的副本來初始化,並且對於rvalues,源的內容將被移動(至多)兩次。

1

變體是對象。它們包含一組類型之一,但它們不是這些類型之一。

對變體的引用是對變體對象的引用,而不是對所包含類型之一的引用。

參考包裝變體可以是你想要什麼:

template<class...Ts> 
using variant_ref=std::variant<std::reference_wrapper<Ts>...>; 

template<typename T> 
using VectorOrSimple = std::variant<T, std::vector<T>>; 
template<typename T> 
using VectorOrSimpleRef = variant_ref<T, std::vector<T>>; 
template<typename T> 
using VectorOrSimpleConstRef = variant_ref<const T, const std::vector<T>>; 

現在存儲VectorOfSimpleConstRef<int>。 (不是const&)。並在構造函數中使用一個。

還修改Print採取const&避免不必要地複製std::vector打印時。