2016-05-27 19 views
2

Ich habe eine Situation, wo ich herausfinden muss, ob ein abgeleitetes Objekt in einem Vektor innerhalb eines anderen Objekts gespeichert ist, und diese Funktion zu funktionieren. Ich kann keinen Weg finden, genau das zu tun, was ich will, oder festzustellen, ob es möglich ist. Ich habe eine Lösung, die funktioniert, die ich einschließe, aber es wäre sauberer, wenn es eine direkte Methode gibt, um das Ziel zu erreichen.C++ Ist es möglich, einen Objekttyp in eine Funktion zu übergeben, die verglichen werden soll?

Dies ist im Wesentlichen, was ich tun möchte:

class IFruit 
{ 
public: 
    virtual ~IFruit(){}; 
}; 
class Apple : public IFruit {}; 
class Banana : public IFruit {}; 
class Orange : public IFruit {}; 

class FruitBowl 
{ 
public: 
    bool HasFruit(datatype?? FruitType) 
    { 
     for (auto iter : m_Fruit) 
     { 
      if (typeof(iter) == typeof(FruitType)) 
      { 
       return (true); 
      } 
     } 
     return (false); 
    } 

    vector< IFruit* > m_Fruit; 
}; 

int main() 
{ 
    FruitBowl Snacks; 
    Snacks.m_Fruit.push_back(new Banana); 
    Snacks.m_Fruit.push_back(new Apple); 
    Snacks.m_Fruit.push_back(new Orange); 

    if (Snacks.HasFruit(Orange)) 
    { 
     cout << "There is an orange available"; 
    } 

    return (0); 
} 

Dies ist eine Abhilfe ist, dass das Ziel erreicht, aber es hat den zusätzlichen Schritt den Rückruf zu schaffen, die ich auszurotten lieben würde:

#include <vector> 
#include <iostream> 
#include <functional> 
using namespace std; 

class IFruit 
{ 
public: 
    virtual ~IFruit(){}; 
}; 
class Apple : public IFruit {}; 
class Banana : public IFruit {}; 
class Orange : public IFruit {}; 

class FruitBowl 
{ 
public: 
    bool HasFruit(function< bool(IFruit*) > fnCompareFruitType) 
    { 
     for (auto iter : m_Fruit) 
     { 
      if (fnCompareFruitType(iter)) 
      { 
       return (true); 
      } 
     } 
     return (false); 
    } 

    vector< IFruit* > m_Fruit; 
}; 

int main() 
{ 
    FruitBowl Snacks; 
    Snacks.m_Fruit.push_back(new Banana); 
    Snacks.m_Fruit.push_back(new Apple); 
    Snacks.m_Fruit.push_back(new Orange); 

    if (Snacks.HasFruit([](IFruit* TestFruit){ return (dynamic_cast< Orange* >(TestFruit) != nullptr); })) 
    { 
     cout << "There is an orange available"; 
    } 

    return (0); 
} 
+0

Wie wäre es, die Funktion HasFruit() zu templatisieren? Ich bin kein C++ Experte, also habe ich es noch nicht genau ausgearbeitet, aber so etwas wie 'template bool HasFruit (T *) const {...}' –

+0

@DaveM. Ich hatte das selbe, sehe meine Antwort. –

Antwort

7

Sie keinen Typ als Runtime-Funktion Parameter übergeben können, aber Sie eine als Kompilierung Template-Parameter einer Funktion Vorlage verwenden können:

#include <vector> 
#include <iostream> 
#include <algorithm> 

class IFruit 
{ 
public: 
    virtual ~IFruit(){}; 
}; 
class Apple : public IFruit {}; 
class Banana : public IFruit {}; 
class Orange : public IFruit {}; 

class FruitBowl 
{ 
public: 
    template <typename T> 
    bool HasFruit() 
    { 
     return std::any_of(m_Fruit.begin(), m_Fruit.end(), 
          [](IFruit* fruit) { 
           return dynamic_cast<T*>(fruit) != nullptr; 
          }); 
    } 

    std::vector< IFruit* > m_Fruit; 
}; 


int main() 
{ 
    FruitBowl Snacks; 
    Snacks.m_Fruit.push_back(new Banana); 
    Snacks.m_Fruit.push_back(new Apple); 
    Snacks.m_Fruit.push_back(new Orange); 

    if (Snacks.HasFruit<Orange>()) 
    { 
     std::cout << "There is an orange available"; 
    } 
} 
+1

Dies ist eine sehr elegante Lösung, +1 für die Einfachheit der Logik –

+0

@MilesBudnek - Vielen Dank für die Klärung der Art übergeben. Ich dachte nicht, dass es gemacht werden könnte, konnte keinen Weg finden, es zu tun, und konnte definitiv nichts aufdecken, das direkt sagte, dass es nicht möglich war. Schwer zu finden Informationen über Dinge, die keinen Grund haben, eine Antwort zu haben. – jgibbs

+0

'HasFruit' kann sogar' const' sein. – Jarod42

2

Für mich wäre Lösung, eine Enum für alle "Fruit-Typen" zu machen und es als Parameter-Datentyp zu verwenden. Sie können einen Vergleich machen dann

+0

Ich brauche den Anrufer vollständig geschieden zu wissen, über Obst-Typen, über die er nicht wissen muss. Es geht hauptsächlich darum, die Arbeitsversion des Codes sauberer zu machen, wenn die Sprache Funktionen hat, um das Endziel zu erreichen – jgibbs

+0

Oh, richtig !! Nun, ich denke darüber nach/erwarte eine bessere Antwort. Gerade jetzt, soweit mein begrenztes Wissen betroffen ist, finde ich keine bessere Lösung als enum_. Ich bin mir sicher, dass es hoffentlich viele geben wird !! –

+0

Obwohl die Verwendung von Enums möglich wäre, würde es den Code erheblich verschmutzen und unnötige Wartung verursachen, da die Typen bereits durch Polymorphie definiert sind. Meine aktuelle Lösung hat nur den einen zusätzlichen Schritt, das Lambda zu liefern, während das Hinzufügen einer Listenliste und das Setzen dieser in allen abgeleiteten Klassen nur überflüssiges Gepäck hinzufügt. – jgibbs

2

So etwas wie das?

#include <iostream> 
#include <vector> 
using namespace std; 

class IFruit { 
    public: 
    virtual  ~IFruit(void) { } 
} ; 

class Apple : public IFruit { }; 
class Banana : public IFruit { }; 
class Orange : public IFruit { }; 

class FruitBowl : public vector<IFruit *> { 
    public: 
    template <typename T> bool HasFruit(void) const { 
     for (auto i : vector<IFruit *>(*this)) { 
      if (typeid(*i) == typeid(T)) return true; 
     } 
     return false; 
    } 
} ; 

int 
main(int, char **) 
{ 
    FruitBowl b; 

    b.push_back(new Apple); 
    b.push_back(new Banana); 
// b.push_back(new Orange); 

    if (b.HasFruit<Orange>()) // thanks M.M.! 
     cout << "found one" << endl; 
    else 
     cout << "no oranges" << endl; 

    return 0; 
} 

Vielleicht gibt es eine sexierere Möglichkeit, den Anruf zu tätigen? (Orange *)0 ist irgendwie hässlich.

+1

Entfernen Sie den Parameter, und rufen Sie die Funktion als 'b.HasFruit ()' –

+0

Ich mag dies. Dies ist das Paradigma, mit dem ich den Pfad von begonnen habe, aber ich konnte mir keinen Weg vorstellen, den Typ zur HasFruit-Funktion zu überführen. Obwohl es immer noch nicht ganz so schön ist, wie ich es letztendlich möchte, ist es definitiv ein großer Schritt näher – jgibbs

+0

'(Orange *) 0' hat mehr Swag Dave :) –

1

Vielleicht:

#include<vector> 
#include<iostream> 
#include<typeinfo> 
#include<memory> 

using namespace std; 

class IFruit 
{ 
public: 
    virtual ~IFruit(){}; 
}; 
class Apple : public IFruit {}; 
class Banana : public IFruit {}; 
class Orange : public IFruit {}; 

class FruitBowl 
{ 
public: 
    //uses tempalte to accept any type 
    template<typename FruitType> 
    bool HasFruit(FruitType fruit) 
    { 
     for (auto iter : m_Fruit) 
     { 
      if (typeid(* iter).hash_code() == typeid(fruit).hash_code()) 
      { 
       return (true); 
      } 
     } 
     return (false); 
    } 

    std::vector<IFruit*> m_Fruit; 
}; 

int main() 
{ 
    FruitBowl Snacks; 
    Snacks.m_Fruit.push_back(new Banana); 
    Snacks.m_Fruit.push_back(new Apple); 
    Snacks.m_Fruit.push_back(new Orange); 

    if (Snacks.HasFruit(Orange())) 
    { 
     cout << "There is an orange available"; 
    } 
    else{ 
     cout<<"No fruit available"; 
    } 

    return (0); 
} 

Live- Demo: http://coliru.stacked-crooked.com/a/37a3e1b4f5567775

+0

Sie können '==' direkt auf typeinfo verwenden, müssen keinen Hash Code machen. –

+0

Ich bevorzuge den Vorschlag von Dave.M, weil es keine Instanziierung einer Orange – jgibbs

+1

Ich dachte, so gut, aber ein kurzer Blick auf http://en.cppreference.com/w/cpp/language/typeid zeigte ' assert (& ti1 == &ti2); // nicht garantiert' aber 'assert (ti1.hash_code() == ti2.hash_code()); // garantiert' –