簡體   English   中英

如何在 C++ 中動態存儲和訪問類型?

[英]How do I store and access a type dynamically in c++?

我知道 C++ 模板,它允許您為多種類型編寫代碼,但是如果我想動態存儲和訪問類型怎么辦? 為什么這在 C++ 中很難做到?

我非常喜歡沒有做這樣的事情:

enum SupportedTypes
{
    IntType,
    FloatType,
    StringType
}

template <typename T>
class ClassThing
{
    public:
        T Value;
        SupportedTypes Type;
}

...

//Not sure if you could even access thing->Type, but regardless, you get the idea...
switch (thing->Type)
{
    case IntType:
        DoSomething(((ClassThing<int>*)thing)->T);
        break;
    case FloatType:
        DoSomething(((ClassThing<float>*)thing)->T);
        break;
    case StringType:
        DoSomething(((ClassThing<string>*)thing)->T);
        break;
}

為什么 c++ 不支持這樣的東西:

int whatIsThis = 5;
type t = typeid(whatIsThis); //typeid exists, but you can't do...:
t anotherInt = 5;

?

我有另一個問題,我更樂觀地得到一個好的答案:如果您選擇走模板化路線,如果您將它一般地存儲在集合中,是否有任何方法來維護該類型? 例如:

vector<ClassThing> things;

(順便說一下,這將給出“類模板的參數列表......丟失”錯誤。)我的猜測是不,這是不可能的,因為上述是不可能的。

謝謝!

如何在 C++ 中動態存儲和訪問類型?

有很多選項可供選擇:

  • 使用運行時多態性,其中您有一個基類,可以為每個受支持的類型提供一些通用功能和派生類; 你經常需要做出一些關於你的接口應該有多“胖”的選擇(提供只對派生類型的子集有意義的基類函數)與強制客戶端使用dynamic_cast<>來恢復/開啟運行時類型

    • 一個特別強大的技術是讓派生類成為同一模板的特定於類型的實例,因為這意味着您可以參數化支持任意類型,即如果它們提供模板期望的使用語義
  • 使用可區分的聯合(基本上,類型標識enum / int與受支持類型的聯合)- std::variant<>是一個不錯的選擇

  • 創建/存儲值捕獲時,您一定會知道它的類型

    • 您可以記錄其typeinfo和地址,然后在稍后訪問變量時,您可以使用typeinfo來測試對象是否屬於特定類型 - 嘗試每個支持的類型直到找到匹配項 - std::any<>是一個很好的選擇為此選擇,

    • 您可以使用函數指針或std::function<>捕獲任意一組特定於類型的操作

為什么 c++ 不支持這樣的東西:

int whatIsThis = 5;
type t = typeid(whatIsThis); //typeid exists, but you can't do...:
t anotherInt = 5;?

它使用decltypeauto

int whatIsThis = 5;
using t = decltype(whatIsThis);
t anotherInt = 5;

auto anotherWhatever = whatIsThis; // another way to create an additional
                                   // variable of the same type

對於運行時多態性,您可能實際上想要閱讀工廠(它創建多種類型的對象中的一種 - 全部派生自某個基本接口 - 給定一些運行時輸入)和克隆函數(創建未知運行時變量的副本)類型)。

如果你選擇走模板化路線,如果你將它一般地存儲在一個集合中,有沒有辦法保持類型: vector<ClassThing> things; (順便說一下,這將給出“ argument list for class template ... is missing ”錯誤。)

如果不實例化模板,您甚至無法從模板創建單個對象,因此也無法擁有整個vector 一種合理的方法是從基類派生模板並將 [smart] 指針或std::reference_wrapper s 存儲到vector的基類。

int x = 5;
decltype(x) y = 4;
auto z = 3;

decltype(a)會給你的類型的a 然后,您可以使用typedef來存儲類型,或者在必要時使用其他函數從類型中刪除引用。

例如:

typedef decltype(a) type1; 
type1 b = 2 * a;

auto使您根本不需要指定類型。

您唯一需要的是在 c++11 模式( -std=c++11 )或更高版本中-std=c++11

至於向量問題, decltype也適用。

我不會竊取答案,但我會為那些試圖做類似事情的人提供我最終使用的方法。 (我正在用 memcpy 編寫我自己的原始序列化和反序列化代碼。)我希望做的是存儲和維護各種類型的排列,而不必創建一堆結構或類,例如(來自我的問題):

template <typename T>
class ClassThing
{
    public:
        T Value;
        SupportedTypes Type;
}

//Then store everything in a:
vector<ClassThing> things;

但是,嘗試將模板化類存儲在向量中將給出“類模板的參數列表......丟失”錯誤,因為正如托尼 D 在他的回答中所說,“您甚至無法從模板創建單個對象沒有實例化它......”我也不想使用任何外部庫,如 boost (用於變體)。

因此,我得出結論,因為我絕對想使用單個集合來存儲所有結構,所以我根本無法使用模板化類。 相反,我決定使用模板化構造函數(僅)和void*作為值,並存儲類型的散列和存儲/復制類型所需的字節數

class ClassThing
{
    public:
        void* Value;

        unsigned long long TypeHash;

        unsigned long long NumberOfBytes;

        template <typename T>
        ClassThing(T passedValue)
        {
            Value = &passedValue;
            TypeHash = typeid(passedValue).hash_code();
            NumberOfBytes = sizeof(T);
        }

        //For strings, do this:
        ClassThing(const char* passedValue, unsigned short passedNumberOfBytes)
        {
            Value = const_cast<char*>(passedValue);
            TypeHash = typeid(char*).hash_code();
            NumberOfBytes = length;
        }
}

不幸的是,這個解決方案丟失了類型,但由於我使用的序列化和反序列化過程是一個簡單的memcpy ,我需要的只是一個指向數據的指針和它使用的字節數。 我在這里存儲類型的散列的原因是我可以在序列化之前執行類型檢查(例如確保浮點數沒有被序列化到 int 應該是的位置)。

對於反序列化過程,我將使用這種技術: https : //stackoverflow.com/a/15313677/1599699

因為我不知道類型,所以我只需要期望 void* 的強制轉換與序列化過程相匹配,盡管我至少可以檢查 NumberOfBytes 值,理想情況下也可以檢查 TypeHash,如果它們可用的話。 在反序列化結束時,我將得到一個 void* 並執行以下操作:

void* deserializedData = ...;
float deserializedFloat = *(float*)&deserializedData;

這當然不是我的問題的理想解決方案,但它允許我做我想做的事,即以極低的內存使用量和極低的維護成本將極高性能的序列化和反序列化為二進制文件。

希望這可以幫助某人!

盡管這不完全是 C++ 答案(而是 C 答案),但它在 C++ 中應該是有效的。

void*類型是指向無類型內存的指針。 基本上,您可以將其轉換為任何類型的指針,然后取消引用。 例子:

int x1 = 42;
long l1 = 123456789L;

void* test = &x1;
int x2 = *(int*)test; // x2 now contains the contents of x1

test = &l1;
long l2 = *(long*)test; // l2 now contains the contents of l1

這絕不是解決問題的最巧妙方法,但它是一種選擇。

進一步閱讀:
https://www.astro.umd.edu/~dcr/Courses/ASTR615/intro_C/node15.html
http://www.circuitstoday.com/void-pointers-in-c
http://www.nongnu.org/c-prog-book/online/x658.html

如果您想要動態類型(在C++11或更高版本中,例如C++14 ),您可以通過創建具有某些聯合的class來創建變體類型:

 class Thing {
   enum SupportedTypes type;
   union {
     intptr_t num; // when type == IntType
     double flo; // when type == FloatType
     std::string str; // when type == StringType
   }
   // etc....
 };

請注意,您需要遵守五規則,並且當type == StringType等時,您可能應該在str顯式調用std::string的析構函數...

一些第三方庫可能會有所幫助: Boost 變體、Qt QVariant等...

暫無
暫無

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

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