2017-09-07 25 views
17

piszę w C++ i chcę przekazać nieznanego typu (znany tylko w czasie wykonywania) do czystej funkcji wirtualnej:C++ przechodzącą nieznanego typu do wirtualnego funkcji

virtual void DoSomething(??? data); 

gdzie DoSomething jest implementacją czystej funkcji wirtualnej w klasie pochodnej.

Planowałem użyć szablonów, ale jak się okazało funkcję wirtualną i szablony nie działają razem: Can a C++ class member function template be virtual?

chcę uniknąć stosując klasę bazową dla wszystkich klas I przekazać do funkcji (coś jak object w języku C#).

Dzięki z góry

+1

można proszę być bardziej konkretnego? W jaki sposób korzystasz z danych wewnątrz funkcji? Jakie są wymagania typu dla danych? Na przykład. istnieje tylko kilka znanych klas lub planujesz zaakceptować dowolną klasę, która ma określoną metodę itp.? – bolov

+1

Twoje pytanie musi zostać nieco zawężone. Czy znasz zakres typów, czy chcesz je automatycznie wydedukować? Najprostszą odpowiedzią będzie "use void *", lepszą odpowiedzią może być opowieść Story Teller. Wszystko zależy od twojego zastosowania. –

+0

Jestem ciekawy, patrząc na odpowiedzi, o to, czy jest jakakolwiek możliwość osiągnięcia tego samego rezultatu bez konieczności rzucania na "coś". Coś jak hermetyzowanie typu w jakiś sposób bez konieczności tworzenia 'doSomething' szablonu i pobierania tego typu z' decltype', tj. Kind of wirtualnej metody fabryki ... – perencia

Odpowiedz

18

Trzeba type erasure. Przykładem tego jest ogólny cel boost::any (i std::any w C++ 17).

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

I wtedy każda podklasa może próba bezpiecznyany_cast w celu uzyskania danych spodziewa.

Można oczywiście wprowadzić własny rodzaj wymazania abstrakcji, jeśli zakres poszukiwanych zachowań jest bardziej ograniczony.

0

coś takiego ?:

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' wymaga co najmniej jednej funkcji wirtualnego członka (prawdopodobnie destruktora), w przeciwnym razie' dynamic_cast' nie będzie działać. – Angew

+0

'Chcę uniknąć używania klasy bazowej dla wszystkich klas, które przekazuję do funkcji (coś jak obiekt w C#).' – perencia

+0

Nie wymaga to klasy bazowej dla każdego obiektu przekazywanego do funkcji, tylko dla opakowania ' Bar'. Choć technicznie jest to "przekazany obiekt", mało prawdopodobne jest to, co oznaczało PO. – AzCopey

1

Jeśli nie chcesz używać impuls/C++ 17 dowolny rozważyć wynikające parametr funkcji „doSometing” z klasy bazowej, a nie dynamiczny odcień właściwy obiekt klasy. W takim przypadku możesz sprawdzić w środowisku wykonawczym, czy masz prawidłowy wskaźnik.

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. 
} 

Najprostszym (ale niebezpieczne!) Sposób byłoby użyć void * pointer + obsada static

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

typu usuwanie nie jest jedyną możliwością.

Możesz być zainteresowany, aby użyć odwiedzający: Take jako argument std :: wariant i odwiedzić go z lambda zawierającym kod szablonu, który chciał realizować:

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