簡體   English   中英

包裝多種可能類型的 C++ 類的正確設計

[英]Proper design for C++ class wrapping multiple possible types

我正在嘗試實現一個 C++ 類,它將包裝一個值(除其他外)。 該值可以是多種類型(字符串、內存緩沖區、數字、向量)中的一種。

實現這一點的簡單方法是做這樣的事情

class A {
    Type type;

    // Only one of these will be valid data; which one will be indicated by `type` (an enum)
    std::wstring wData{};
    long dwData{};
    MemoryBuffer lpData{};
    std::vector<std::wstring> vData{};
};

這感覺不優雅,就像浪費內存一樣。

我也嘗試將它作為聯合來實現,但它帶來了大量的開發開銷(定義自定義析構函數/移動構造函數/復制構造函數),即使有所有這些,我仍然遇到了一些錯誤。

我還考慮過將A設為基類並為其可以容納的每個可能值創建派生類。 這也感覺它不是解決問題的好方法。

我的最后一種方法是讓每個成員成為std::optional ,但這仍然會增加一些開銷。

哪種方法最好? 或者是否有另一種設計比這些設計更好?

使用std::variant 它是類型安全的,經過測試並且對於有限數量的可能類型來說完全正確。 它也擺脫了類型枚舉。

class A {
    std::variant<std::wstring, long, MemoryBuffer, std::vector<std::wstring>> m_data{}; // default initializes the wstring.
public 
    template<class T>
    void set_data(T&& data) {
        m_data = std::forward<T>(data);
    }

    int get_index() { // returns index of type.
        m_data.index();
    }

    long& get_ldata() {
        return std::get<long>(m_data); // throws if long is not the active type
    }

    // and the others, or

    template<class T> 
    T& get_data() { // by type
        return std::get<T>(m_data);
    }

    template<int N>
    auto get_data() { // by index
        return std::get<N>(m_data);
    }
};

// using:
A a;
a.index() == 0; // true
a.set_data(42); 
a.index() == 1; // true
auto l = a.get<long>(); // l is now of type long, has value 42
a.get<long>() = 1;
l = a.get<1>();

PS:這個例子甚至不包括std::variant最酷的(在我看來)特性: std::visit我不確定你想用你的班級做什么,所以我不能創建一個有意義的例子。 如果你告訴我,我會考慮的。

你基本上想要QVariant沒有 Qt 的其余部分,然后:)?

正如其他人所提到的,您可以使用std::variant並將using MyVariant = std::variant<t1, t2, ...>放在一些常見的標題中,然后在需要的任何地方使用它。 這並不像您想象的那么不雅 - 要傳遞的特定類型僅在一個地方提供。 這是在不構建可以封裝對任何類型對象的操作的元類型機制的情況下執行此操作的唯一方法。

這就是boost::any用武之地:它正是這樣做的。 它包裝概念,因此支持實現這些概念的任何對象。 需要哪些概念取決於您,但通常您希望選擇足夠多的概念以使該類型可用且有用,但不要太多,以免過早地排除某些類型。 這可能是要走的路,你有: using MyVariant = any<construct, _a>; 然后(其中, construct是一個合約列表,其中的一個示例是文檔中的示例,而_a是來自boost::type_erasure的類型占位符。

std::variantboost::any之間的根本區別在於, variant是在具體類型上參數化的,而any在類型綁定到的合同上參數化的。 然后, any 將愉快地存儲滿足所有這些合同的任意類型。 您為變體類型定義別名的“中心位置”將隨着variant不斷增長,因為您需要封裝更多類型。 對於any ,中心位置將大部分是靜態的,並且很少更改,因為更改合同要求可能需要對所攜帶的類型以及使用點進行修復/調整。

暫無
暫無

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

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