2009-07-17 13 views
2

Update: Bearbeitetes Codebeispiel zur Verwendung von AutoA für die Problemumgehung (was die ursprüngliche Absicht war). Realisierte dies, nachdem er rlbonds Antwort gesehen hatte.std :: auto_ptr Kompilierungsproblem in Visual Studio 6.0

Ich versuche, die Verwendung von auto_ptr in meinem Code aus diesem Thread basierend auf Empfehlungen zu übernehmen: Jedoch

Express the usage of C++ arguments through method interfaces

, erhalte ich einige unerwartete Fehler kompilieren, wenn sie mit Visual Studio 6.0 zu kompilieren. Es hat ein Problem beim Umgang mit Zuweisungen/Kopien eines std::auto_ptr eines abgeleiteten Typs zu einem std::auto_ptr des Basistyps. Ist das ein Problem für meinen Compiler?

Ich weiß, es gibt eine starke Empfehlung, Boost zu verwenden, aber auf meinem Projekt ist es keine Option. Wenn ich immer noch auto_ptr verwenden möchte, bin ich gezwungen, die Problemumgehung des Aufrufs std::auto_ptr::release() zu verwenden? Von dem, was ich bis jetzt gesehen habe, führt dieses Problem zu einem Compiler-Fehler, so dass es leicht zu fangen ist. Könnte die Annahme der Vereinbarung, die Freigabe zu einem "auto_ptr" des Basistyps zuzuweisen, mich jedoch allen Wartungsproblemen aussetzen? Vor allem, wenn es mit einem anderen Compiler erstellt wurde (vorausgesetzt, andere Compiler haben dieses Problem nicht).

Wenn die Problemumgehung release() aufgrund meiner Umstände nicht gut ist, sollte ich auf eine andere Konvention zur Beschreibung der Eigentumsübertragung zurückgreifen?

Das folgende Beispiel veranschaulicht das Problem.

#include "stdafx.h" 
#include <memory> 

struct A 
{ 
    int x; 
}; 

struct B : public A 
{ 
    int y; 
}; 

typedef std::auto_ptr<A> AutoA; 
typedef std::auto_ptr<B> AutoB; 

void sink(AutoA a) 
{ 
    //Some Code.... 
} 

int main(int argc, char* argv[]) 
{ 
    //Raws to auto ptr 
    AutoA a_raw_to_a_auto(new A()); 
    AutoB b_raw_to_b_auto(new B()); 
    AutoA b_raw_to_a_auto(new B()); 

    //autos to same type autos 
    AutoA a_auto_to_a_auto(a_raw_to_a_auto); 
    AutoB b_auto_to_b_auto(b_raw_to_b_auto); 

    //raw derive to auto base 
    AutoB b_auto(new B()); 

    //auto derive to auto base 
    AutoA b_auto_to_a_auto(b_auto); //fails to compile 

    //workaround to avoid compile error. 
    AutoB b_workaround(new B()); 
    AutoA b_auto_to_a_auto_workaround(b_workaround.release()); 

    sink(a_raw_to_a_auto); 
    sink(b_raw_to_b_auto); //fails to compile 

    return 0; 
} 

Compile Fehler:

Compiling... 
Sandbox.cpp 
C:\Program Files\Microsoft Visual Studio\MyProjects\Sandbox\Sandbox.cpp(40) : error C2664: '__thiscall std::auto_ptr<struct A>::std::auto_ptr<struct A>(struct A *)' : cannot convert parameter 1 from 'class std::auto_ptr<struct B>' to 'struct A *' 
     No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called 
C:\Program Files\Microsoft Visual Studio\MyProjects\Sandbox\Sandbox.cpp(47) : error C2664: 'sink' : cannot convert parameter 1 from 'class std::auto_ptr<struct B>' to 'class std::auto_ptr<struct A>' 
     No constructor could take the source type, or constructor overload resolution was ambiguous 
Error executing cl.exe. 

Sandbox.exe - 2 error(s), 0 warning(s) 
+4

Ernsthaft? ein 11 Jahre alter Compiler? Man könnte meinen, die Leute würden weiterziehen. – shoosh

+0

Sie würden denken, dass es etwas geben würde, um sich zu bewegen. 10 ist die neue 6, aber 10 ist noch in der Beta. –

Antwort

5

Die erste ist einfach:

AutoA b_auto_to_a_auto(b_auto); //fails to compile 

Diese auf VC6 versagt, da es Funktionsschablonen Mitglied erfordert, Standardbibliothek etwas VC6 nicht unterstützt. Es kompiliert jedoch auf standardkonformen Compilern.

Umgehung:

AutoA b_auto_to_a_auto(b_auto.release()); 

Das zweite ist viel subtiler :)

sink(b_raw_to_b_auto); //fails to compile 

Dieser nicht auf einem standardkonformen Compiler kompilieren sollte, weil es eine implizite Konvertierung geht. Der Compiler stellt sich die oben in

sink(std::auto_ptr<A>(b_raw_to_b_auto)); 

jedoch sink nimmt std::auto_ptr<A>vonWert, so dass die temporäre std::auto_ptr<A> muss implizit vom Compiler erstellt Kopie konstruierten in das Argument zu sink sein. Nun, solche Provisorien sind rvalues ​​. Rvalues ​​binden nicht an nicht-konstante Referenzen, aber std::auto_ptr 's Copy Constructor "nimmt sein Argument von nicht-const Referenz.

Da gehst du - kompilieren Fehler. AFAICS das ist normenkonformes Verhalten. C++ - 0x "move semantics" wird das beheben, indem man einen "copy constructor" hinzufügt, der eine rvalue-Referenz nimmt, obwohl ich nicht sicher bin, wie viel Liebe std::auto_ptr in Zukunft noch erhalten wird, was mit std::shared_ptr und allem.

Behelfslösung für die zweite:

AutoA tmp(b_raw_to_b_auto/*.release() for VC6*/); 
sink(tmp); 
+0

Danke für die Antwort. Ich hatte das Gefühl, dass es mit den Vorlagen von VC6 zu tun hatte, ich hatte Probleme damit. Ich hatte erwartet, dass es ein Problem mit VC6 war, interessant, um die Gründe dafür herauszufinden. Ich wusste nichts über dieses Detail, das ist eine gute Information. Nachdem ich nach Hause gekommen war, konnte ich die gleichen Build-Ergebnisse erzielen, die mit dem übereinstimmen, was Sie sagen, dass sie dem Standard entsprechen. Danke an alle anderen für deine Antworten. –

4
AutoA b_auto_to_a_auto(b_auto); //fails to compile 

sink(b_raw_to_b_auto); //fails to compile 

Pavel Minaev weist darauf hin, etwas, was ich nicht wusste, eigentlich:

Der erste Aufruf kompilieren sollte, weil es eine implizite Konvertierung von a B * zu einem A *. Die zweite wird jedoch nicht kompilieren. Der folgende Wille:

sink(static_cast<AutoA>(b_raw_to_b_auto)); 

VC6 ist berüchtigt dafür, nicht sehr gut mit Vorlagen zu sein.

Ich schlage vor, dass Sie Ihre Code-Basis zu einer, die tatsächlich funktioniert und RAII-Techniken, insbesondere boost::shared_ptr beginnen. Ich weiß, dass du sagst, dass du es nicht kannst, und ich weiß, dass es schwierig ist, aber du wirst praktisch keine Speicherlecks und viele, viel weniger Fehler haben.

Dann noch einmal, vielleicht sogar ohne volle Funktionalität können Sie auto_ptr verwenden?

+0

Mein Verständnis von auto_ptr war, dass es eine ähnliche Semantik wie ein normaler Zeiger hatte.Zum Beispiel, wenn ich eine Funktion von Leere Senke (A * a) hätte; Ich könnte einen Anruf wie folgt machen: B * b = neue B(); Waschbecken (b); Wenn dies eine Beschränkung ist, die nicht vermieden werden kann, wenn Sie std :: auto_ptr verwenden, dann muss ich die Nützlichkeit für das, was ich versuche, oder meinen Ansatz überdenken. Ihr Kommentar gegen Senke ist in meinem Fall das gewünschte Verhalten, da ich versuche, den Besitz des Objekts, auf das der übergebene auto_ptr verweist, zu übertragen. –

+0

Alles, was ich sage, ist, dass genau ein Auto_ptr in der Lage ist, ein Objekt zu besitzen, und dass die Zuweisung oder das Kopieren das Eigentum überträgt. Viele Leute glauben, dass auto_ptr deswegen ein schlechtes Design ist. Zum Beispiel kann auto_ptr nicht in STL-Containern verwendet werden. Solange Sie wissen, was Sie tun, ist es in Ordnung, aber die Zuweisung funktioniert nicht wie ein normaler Zeiger. Ich weiß, du hast gesagt, Boost ist keine Option, aber Boosts shared_ptr ist wirklich das, was du willst, wenn du eine gemeinsame Eigentümerschaft brauchst. – rlbond

+0

"Sie sind nicht verwandte Typen, obwohl A und B verwandt sind." und "Wenn Sie A und B polymorph behandeln möchten, verwenden Sie einfach ein auto_ptr ." Basierend darauf werde ich annehmen, dass boost :: shared_ptr das gleiche Problem haben würde? –

2

Es gibt zwei Probleme hier. Vor allem dies:

AutoA b_auto_to_a_auto(b_auto); 

Es ist perfekt Standard kompatibel und kompilieren soll. Lass mich erklären warum. Der ISO C++ Standard spezifiziert (20.4.5.1 [lib.auto.ptr.cons]/4-6) den folgenden Konstruktor für auto_ptr<X> (neben anderen);

template<class Y> auto_ptr(auto_ptr<Y>&) throw(); 

Beachten Sie, dass Y von X hier eine andere Art ist. Und die Norm weiterhin sagt:

Requires: Y* can be implicitly converted to X*.

Das einzige, was darauf zu achten, hier ist, dass Konstruktorargument ist eine Referenz-zu-nicht-const. Für Ihren Fall ist das kein Problem (da Sie dort eine nicht-konstante Variable übergeben), aber es wird wichtig für den nächsten Teil. Um hier zu schließen: Was Sie sehen, ist Nicht-Standard-Verhalten in VC6. Es sollte kompilieren auf einem kompatiblen Compiler (und wird auf VC7 und höher kompilieren). Nun zum zweiten Sache:

sink(b_raw_to_b_auto); //fails to compile 

Dieser ist eigentlich perfekt erklärt mmutz, also werde ich nicht ins Detail gehen hier - seine Antwort sehen. Zum Schluss: Ja, diese Zeile sollte nicht kompiliert werden, und wird nicht in einem kompatiblen Compiler (oder VC6, wie Sie herausgefunden haben).

Verwandte Themen