2016-06-07 7 views
-1

Wenn Sie eine Klasse Base mit virtuellen Methoden und eine Klasse Implementierung haben, die die virtuellen Methoden implementiert, gibt es eine Möglichkeit, std :: shared_ptr < Implementierung zu std :: shared < Base> &? Der Compiler erlaubt dies für Const-Verweise, aber für nicht-const-Verweise schlägt es wie in "Case A" in dem folgenden Code fehl. Gibt es einen einfachen Weg, dies zu tun?Gibt es eine bessere/sichere Möglichkeit, eine nicht-konstante Referenz von shared_ptr auf eine Basisklasse zu übertragen?

Wenn nicht, wie sicher ist meine Problemumgehung "fraglich_cast" in Fall B?

#include <iostream> 
#include <memory> 

class Base 
{ 
public: 
    virtual void set_value(int x) = 0; 
}; 

class Implementation : public Base 
{ 
public: 
    Implementation() : m_value(0) { } 
    void set_value(int x) override 
    { 
    m_value = x; 
    } 
    int get_value() const 
    { 
    return m_value; 
    } 
private: 
    int m_value; 
}; 


void do_something(std::shared_ptr<Base>& base) 
{ 
    base->set_value(5); 

    /// Code like this makes the non-const argument necessary 
    base = std::make_shared<Implementation>(); 
} 

template <class T, class U> 
std::shared_ptr<T>& questionable_cast(std::shared_ptr<U>& u) 
{ 
    /// This code is here to assure the cast is allowed 
    std::shared_ptr<T> tmp = u; 
    (void)tmp; 

    return *reinterpret_cast<std::shared_ptr<T>*>(&u); 
} 

int main() 
{ 
    std::shared_ptr<Implementation> a = std::make_shared<Implementation>(); 

    // The following line causes a compiler error: 
    // invalid initialization of reference of type ‘std::shared_ptr<Base>&’ ... 
    // do_something(a); 
    // do_something(std::dynamic_pointer_cast<Base>(a)); 

    // This is the workaround 
    do_something(questionable_cast<Base>(a)); 

    std::cerr << "a = " << a->get_value() << std::endl; 

    return 0; 
} 
+0

Bonus Frage: Warum wird dies downvoted? – bofjas

+0

Warum versuchen Sie, dies mit einer Referenz zu tun, anstatt nur eine Kopie zu erstellen, wie Sie es in der Funktion "fragliche_Sendung" tun? Wenn Sie eine Referenz verwenden, wird der Zähler nicht erhöht und die Referenz kann ungültig gemacht werden. –

+2

Ich habe nicht runtergestimmt, aber mögliche Gründe: * Warum * willst du das machen? Die Verwendung von reinterpret_cast ist eine Verletzung der strengen Aliasing-Regeln und somit ein undefiniertes Verhalten. –

Antwort

2

Zwei offensichtliche Lösungen für das Problem, wie ursprünglich gefragt: 1. Machen Sie do_something nimmt eine konstante Referenz auf einen shared_ptr (oder einen Shared_ptr nach Wert). 2. Erstellen Sie einen Namen shared_ptr und übergeben einen Verweis auf das: Eg

int main() 
{ 
    std::shared_ptr<Implementation> a = std::make_shared<Implementation>(); 
    std::shared_ptr<Base> b = a; // This conversion works. 
    do_something(b); // Pass a reference to b instead. 
    return 0; 
} 

Ihre questionable_cast Funktion ist eine Verletzung der strengen Aliasing Regeln und ruft nicht definiertes Verhalten. Es ist sehr wahrscheinlich, dass es in ersten Tests funktioniert, und dann wird eine neue Version des Compilers die Optimierung ein wenig ankurbeln, und es wird während einer Demo versagen.

den Fall zu behandeln, wo do_something den Zeiger ändert:

int main() 
{ 
    std::shared_ptr<Implementation> a = std::make_shared<Implementation>(); 
    std::shared_ptr<Base> b = a; // This conversion works. 
    do_something(b); // Pass a reference to b instead. 
    const auto aa = std::dynamic_pointer_cast<Implementation>(b); 
    if (aa) 
     a = aa; 
    else 
     ; // Handle the error here 
    return 0; 
} 

Wenn do_something garantiert einen Zeiger aus dem gleichen abgeleiteten Typ zurückzukehren, auch wenn sie nicht den gleichen Zeiger zurückgibt, wickeln Sie es in einer Vorlage Funktion:

template <typename T> 
void do_something_ex(std::shared_ptr<T>& a) 
{ 
    std::shared_ptr<Base> b = a; 
    do_something(b) 
    a = std::dynamic_pointer_cast<T>(b); 
    if (!a) 
     throw_or_assert; 
} 
+0

Ok. Es tut uns leid. Ich habe ein wichtiges Merkmal von dem, was das tut, vergessen. Es kann auch shared_ptr ändern. Also würde keiner Ihrer Vorschläge funktionieren. – bofjas

+0

@bofjas: Es kann immer noch, und solche Änderungen werden in 'b' reflektiert. Wenn Sie möchten, dass sie in "a" reflektiert werden, kopieren Sie "b" wieder hinein. –

+0

Ja, der Grund, warum ich das nicht wollte, ist, weil ich dann einen dynamic_pointer_cast machen und das Ergebnis überprüfen muss, so dass es jede Menge Code gibt, an dem ich die Funktion anrufe, aber ich wusste nicht, dass das tatsächlich der Fall war Er sagte mir, was mit meinem Ansatz unsicher war. Ich denke, ich stecke mit dieser Lösung fest. Danke für die Hilfe! – bofjas