2017-04-19 54 views
2

根據這些定義,並沿着這些線路一些代碼,用於說明目的:如何在不知道底層類型的情況下從模板父類中獲取值?

class Child 
{ 
    private: 
    std::string name; 

    public: 
    Child(const std::string & name) name(name) 
    { } 
} 

template<typename T> 
class Parent : public Child 
{ 
    private: 
    T data; 

    public Parent(const std::string & name, const T & data) : Child(name), data(data) 
    { } 

    inline GetData() const 
    { 
     return this->data; 
    } 
} 

std::vector<std::unique_ptr<Child>> values; 
values.emplace_back(std::make_unique<Parent<int>>("value a", 4)); 
values.emplace_back(std::make_unique<Parent<std::string>>("value b", "test")); 

for (const auto & v : values) 
{ 
    // I want to access the int and string here! 
} 

如何將決定從這裏基類的父類中使用的類型?

我一起工作的具體情況其實是,我有一堆包含有關存儲過程的SQL參數,是應該被包含在std::vector<std::unique_ptr<SqlParameterBase>>並傳遞到代表一個類SqlParameter<T>模板信息SqlParameterBase類一個存儲過程配置,以便它可以被調用。

但是這裏的根本問題是我需要訪問底層數據,我想避免在基類中定義一個包含指向數據的指針的void *。我更喜歡它是儘可能安全的。

我願意接受其他設計或方法來解決這個問題,所以如果你覺得它更好,請隨時提出一些完全不同的建議。

+0

您需要'dynamic_cast',但是您真正想要的是變體類型。 – juanchopanza

+0

是的,我知道我需要做一個動態轉換爲父類型。問題是,如果不以硬編碼的方式測試每種類型,我無法確定要投射到哪種類型。最後,我基本上需要能夠確定數據的大小,比如向量中元素的數量或字符串的長度;這些成員函數根據類型進行更改。我想過一個模板,只是假設T :: size存在,但如果函數的行爲在類型之間有不同的定義,則可能會有風險。 –

+0

如果您需要確定數據的「大小」,如何將'virtual std :: size_t size()const = 0;'添加到'Child'? – aschepler

回答

0

您想要只在派生類型中存在的子承諾功能(返回類型化數據)。在繼承下,這是抽象的功能。所以一組推測虛擬訪問器。這裏並不需要upcasts。

我會說。如果你不希望孩子承諾它返回的東西,只是它返回東西那麼它看起來像你的界面太寬泛了,你把界面放在消耗界面的代碼中,而是用推測性轉換等等。

雖然Child中定義的接口可以與一組Child連接,或者它們有一些特殊的功能只能在Derived中使用,並且必須以此方式說出。

作爲一個附錄,你可能會考慮一個std::vector<std::variant<type1, type2, ...>>而不是完全錯誤的容器中的類型信息? std :: variant是C++ 17 ofc,因此可能不適合,但有第三方實現 - 增強變體等。

+0

你說抽象的功能,它讓我覺得。在我的父類中有一個'virtual size_t GetSize()= 0'的適當設計,然後在許多專門用於支持的支持數據的子類中實現它?例如,我會有一個'StringChild'和一個'IntChild',並且每個實現他們自己的方式來獲取他們的數據和數據大小。然後,我會在循環內切換並使用枚舉來跟蹤數據類型,然後在需要獲取數據時動態轉換它。看起來像很多代碼,但它使我的界面更窄。 –

+0

經過一番閱讀和搞亂之後,'std :: variant'實際上很適合我的需求。我已經將我的功能簡化爲每種SQL類型的一組受支持的類型,並根據查詢中參數的預期SQL類型嘗試轉換少數受支持的數據類型。 –

0

試試這個程序(這個問題:我將如何確定在父類這裏使用從基類類型?):

輸出打印的類型.. 但是更多的工作是需要更好看

value a of type:i 
value b of type:NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE 

這裏「i」表示第一行中的整數。

第二個在其中有「basic_string」。但它周圍有些垃圾。

我發佈這個,因爲它可能會觸發更好的想法。

爲修改的代碼添加了註釋。

//g++ 5.4.0 

#include <iostream> 
#include <string> 
#include <vector> 
#include <memory> 

//CREATED ANOTHER VARIABLE AND FUNCTION FOR TYPENAME. 
//MODIFIED CONSTRUCTOR ACCORDINGLY. 
class Child 
{ 
    private: 
    std::string name; 
    std::string tyName; 

    public: 
    Child(const std::string & name,const std::string &tyname):name(name), tyName (tyname) 
    { } 

    std::string getName() 
    { 
     return name; 
    } 

    std::string getTypeName() 
    { 
     return tyName; 
    } 
}; 

//MODIFIED CONSTRUCTOR TO PASS THE TYPENAME TO PARENT CLASS. 
template<typename T> 
class Parent : public Child 
{ 
    private: 
    T data; 

    public: 
    Parent(const std::string & name, const T & data) : Child(name,typeid(data).name()), data(data) 
    { } 

    inline T GetData() const 
    { 
      return this->data; 
     } 
}; 



int main() 
{ 
    std::vector<std::unique_ptr<Child>> values; 
    values.emplace_back(std::make_unique<Parent<int>>("value a", 4)); 
    values.emplace_back(std::make_unique<Parent<std::string>>("value b", "test")); 


    //GET THE POINTER OF YOUR BASE CLASS. 
    //PRINT THE TYPE NAME. 
    for (const auto & v : values) 
    { 
     Child *p = dynamic_cast<Child*>(v.get()); 
     std::cout<<p->getName()<<" of type:"<<p->getTypeName()<<std::endl;   
    } 

    return 0; 
} 
相關問題