2017-09-07 41 views
17

我正在寫在C++和我想通過一個未知的類型(只在運行時已知的),以純虛函數:C++傳遞未知類型到一個虛擬函數

virtual void DoSomething(??? data); 

其中DoSomething是一個實現一個派生類中的純虛函數。

我計劃使用的模板,但因爲它變成虛函數和模板不一起工作:Can a C++ class member function template be virtual?

我想避免使用基類我傳遞給函數的所有類(類似object在C#中)。

在此先感謝

+1

可以請你更具體?你如何使用函數內的數據?數據類型的要求是什麼?例如。有幾個已知的類,或者你打算接受任何具有特定方法的類? – bolov

+1

您的問題需要縮小一點。你知道類型的範圍,你想讓它們被自動推斷嗎?最簡單的答案是「使用void *」,更好的答案可能是Story Teller的答案。這一切都取決於你的用例。 –

+0

我很好奇,看着答案,關於是否有可能實現相同的結果,而不必在'doSomething'上做任何演員。像某種方式封裝類型,而不必將'doSomething'作爲一個模板,並用'decltype',即一種虛擬工廠方法來檢索該類型... – perencia

回答

18

你需要type erasure。一個例子是通用的boost::any(和C++ 17中的std::any)。

virtual void DoSomething(boost::any const& data); 

然後每個子類可以爲了得到它預計數據嘗試安全any_cast

void DoSomething(boost::any const& data) { 
    auto p = any_cast<std::string>(&data); 

    if(p) { 
    // do something with the string pointer we extracted 
    } 
} 

如果您尋求的行爲範圍更受限制,您當然可以推出自己的類型擦除抽象。

0

這樣的事情?:

class Foo 
{ 
    virtual ~Foo() = 0; 
}; 

template <typename T> 
class Bar : public Foo 
{ 
    T object; 
} 

... 

virtual void DoSomething(Foo* data) 
{ 
    Bar<int>* temp = dynamic_cast<Bar<int>*>(data); 
    if (temp) 
     std::count<<temp->object; 
} 
+6

'Foo'至少需要一個虛擬成員函數(可能是析構函數),否則'dynamic_cast'將不起作用。 – Angew

+0

'我想避免爲傳遞給函數的所有類使用基類(類似於C#中的對象)' – perencia

+0

這不需要傳遞給函數的每個對象的基類,只適用於包裝器' Bar'。雖然這在技術上是「傳入對象」,但它不可能是OP的含義。 – AzCopey

1

如果你不想使用升壓/ C++ 17任,考慮從基類派生的「doSometing」功能的參數,並做動態轉換到正確的類對象。在這種情況下,你可以在運行時檢查你是否有一個有效的指針。

class param{ 
public: 
    virtual ~param(){}; 
}; 

template <typename T> 
struct specificParam:param{ 
    specificParam(T p):param(p){} 
    T param; 
}; 


class Foo 
{ 
public: 
    virtual void doSomething(param* data) = 0; 
}; 

template <typename T> 
class Bar : public Foo 
{ 
public: 
    virtual void doSomething(param* data){ 
     specificParam<T> *p = dynamic_cast<specificParam<T> *>(data); 

     if (p != nullptr){ 
      std::cout<<"Bar got:" << p->param << "\n"; 
     } 
     else { 
      std::cout<<"Bar: parameter type error.\n"; 
     } 
    } 
}; 

int main(){ 
    Bar<char> obj1; 
    Bar<int> obj2; 
    Bar<float> obj3; 

    specificParam<char> t1('a'); 
    specificParam<int> t2(1); 
    specificParam<float> t3(2.2); 

    obj1.doSomething(&t1); //Bar got:a 
    obj2.doSomething(&t2); //Bar got:1 
    obj3.doSomething(&t3); //Bar got:2.2 

    // trying to access int object with float parameter 
    obj2.doSomething(&t3); //Bar: parameter type error. 
} 

最簡單的(但不安全的!)的方法是用void *指針+靜態澆鑄

class Foo 
{ 
public: 
    virtual void doSomething(void* data) = 0; 
}; 

template <typename T> 
class Bar:public Foo 
{ 
public: 
    virtual void doSomething(void* data){ 
     T* pData = static_cast<T*>(data); 
     std::cout<<"Bar1 got:" << *pData << "\n"; 
    } 
}; 

int main(){ 

    Bar<char> obj1; 
    Bar<int> obj2; 
    Bar<float> obj3; 

    char c = 'a'; 
    int i = 1; 
    float f = 2.2; 

    obj1.doSomething(&c); // Bar1 got:a 
    obj2.doSomething(&i); // Bar1 got:1 
    obj3.doSomething(&f); // Bar1 got:2.2 

    //obj2.doSomething(&c); // Very bad!!!  
} 
1

類型擦除不是唯一的可能性。

你可能有興趣使用訪問者模式:採取作爲參數的的std ::變種,幷包含你想實現模板代碼拉姆達參觀:

virtual void doSomething(std::variant<int,float/*,...*/> data) 
    { 
    visit([=](auto v){/*...*/;},data); 
    }