2016-03-30 3 views
1

Der Kontext: Ich schrieb einige Tools für die Archivierung von Daten, in ähnlicher Weise von Archiven von Boost. Dann wird, wie beispielsweise kann ich diese Art von Code schreiben:Vermeiden Sie Zuordnung von abstrakten Typ mit Vorlagen

class A 
{ 
    private: 
    double a; 
    public: 
    A() : a(3.14159) 
    {} 
    A(const A& a_) : a(a_.a) {} 
    virtual ~A() 
    {} 
    virtual A* clone() const = 0; // Then, A is virtual 

    virtual void save(O_Archive& oa) const // 
    {          // 
     oa << this->a;      // INTERESTING 
    }          // PART OF THE 
    virtual void load(I_archive& ia)  // CLASS 
    {          // 
     ia >> this->a;      // 
    }          // 
}; 
O_Archive& operator << (O_Archive& oa, const A& a) 
{ 
    a.save(oa); 
    return oa; 
} 
I_Archive& operator >> (I_Archive& ia, A& a) 
{ 
    a.load(ia); 
    return ia; 
} 

class B : public A 
{ 
    private: 
    double b; 
    public: 
    B() : A(), b(1.0) {} 
    B(const B& b_) : A(b_), b(b_.b) {} 
    virtual ~B() {} 
    virtual A* clone() const 
    { 
     return new B(*this); 
    } 

    void save(O_Archive& oa) const // 
    {        // 
     this->A::save(oa);   // 
     oa << this->b;    // INTERESTING 
    }        // PART OF THE 
    void load(I_Archive& ia)  // CLASS 
    {        // 
     this->A::load(ia);   // 
     ia >> this->b;    // 
    }        // 
}; 

// Consider classes 'C' and 'D' similar to 'B' 

void example_save(O_Archive& oa) 
{ 
    A* p1 = new B; 
    A* p2 = new C; 
    D* p3 = new D; 
    oa << Archive::declare_derived<A,B,C,D>(); 
    oa << p1 << p2; // Automatically detect the inheritance 
    oa << p3; // Store the instance as a usual pointer 
} 
void example_load(I_Archive& ia) 
{ 
    A* p1 = 0; 
    A* p2 = 0; 
    B* p3 = 0; 
    ia << Archive::declare_derived<A,B,C,D>(); 
    ia >> p1 >> p2; 
    ia >> p3; 
} 

Wo liegt das Problem? Dies funktioniert mit mehreren Funktionen wie der folgenden load_pointer-Funktion in der Klasse I_Archive, die dafür zuständig ist zu überprüfen, ob der Zeiger zugewiesen wurde, ob es sich um eine Instanz mit einem abgeleiteten Typ oder einfach um einen gewöhnlichen Zeiger handelte.

template <typename T> 
void I_Archive::load_pointer(T*& p) 
{ 
    delete p; 
    bool allocated; 
    this->load_bool(allocated); 
    if(allocated) 
    { 
     bool inheriance; 
     this->load_bool(inheriance); 
     if(inheriance) 
     { 
      unsigned long int i; 
      this->load_unsigned_long_int(i); 
      p = boost::static_pointer_cast< const Archive::allocator<T> >(this->alloc[&typeid(T)][i])->allocate(); 
     } 
     else 
      p = new T; // ERROR AT THIS LINE 
     *this >> *p; 
    } 
    else 
     p = 0; 
} 

Mein Problem: Eigentlich hat mein Code nicht mit dem folgenden Fehler auf der Linie kompilieren p = new T;:

error: cannot allocate an object of abstract type ‘A’.

ich zuerst überrascht war, aber ich wirklich gut verstehen, warum ich habe Dieser Fehler: Wenn die Funktion load_pointer auf p1 aufgerufen wird, wird die Anweisung new T wird new A, die verboten ist, auch wenn die Anweisung nie ausgeführt wird, wenn der Typ abs ist Traktat.

Meine Frage: Ich kann keinen Weg finden, Vorlagen korrekt zu verwenden, um meinen Fehler zu vermeiden. Gibt es eine mögliche Problemumgehung das zu tun oder zu dem Compiler zu sagen "Ich weiß, was ich tue, Sie müssen nie einen abstrakten Typ instanziieren"?

Wichtiger Hinweis: Ich kann aus Kompatibilitätsgründen nicht mit C++ 11 arbeiten.

+0

Es erscheint Typ 'T' im Fehlerfall ist' A'. –

+0

Vielleicht könnten Sie einfach Ihre 'load_pointer'-Funktion für den speziellen Fall von A überladen, in dem Sie das Zeug spezifisch für A. –

+0

@ThomasBenard: Es ist nicht die Verantwortung von 'Archive', alle abstrakten Klassen der Anwendung zu kennen. Es impliziert, dass ich jedes Mal, wenn ich eine neue abstrakte Klasse hinzufüge, die Klasse "Archivieren" bearbeiten müsste. Es ist ein sehr schlechtes Design. – Caduchon

Antwort

2

Die Eigenschaft, die Sie suchen, ist std::is_abstract. Wie Sie erwähnt haben, können Sie C++ 11 nicht verwenden, aber Sie können seine implementation von Boost verwenden.

Sie können dann is_abstract zusammen verwenden mit std::enable_if (auch hier aufgrund Ihrer Einschränkung von nicht C++ 11 verwenden, können Sie die Beispielimplementierung von here nehmen nur), um diese in ähnlicher Weise zu implementieren:

#include <iostream> 
#include <type_traits> 

struct A { 
    virtual void f() = 0; 
}; 

struct B : A { 
    void f() override {} 
}; 


template<typename T> 
std::enable_if_t<std::is_abstract<T>::value, T*> allocate() 
{ 
    return nullptr; 
} 

template<typename T> 
std::enable_if_t<!std::is_abstract<T>::value, T*> allocate() 
{ 
    return new T; 
} 

// Test 
template<typename T> 
T* test_alloc() 
{ 
    return allocate<T>(); 
} 

int main() 
{ 
    std::cout << test_alloc<A>() << "\n"; // Outputs nullptr 
    std::cout << test_alloc<B>() << "\n"; // Outputs an address 
} 

LIVE

Verwandte Themen