Wie erkenne ich const Referenz auf temporäre Probleme bei der Kompilierung oder Laufzeit?

8

Ich habe kürzlich festgestellt, dass die meisten Fehler in meinen C ++ Programmen sind ein Formular wie das folgende Beispiel:

%Vor%

AUSGABE: 1000

Ich würde erwarten, dass dieses Programm 123 ausgibt (der Wert von x.y.z.n wird gesetzt) get_x ()) aber die Erstellung von "Big b" überschreibt das temporäre Z. Als Ergebnis ist der Verweis auf das temporäre Z im Objekt Y jetzt mit Big b überschrieben, und daher ist die Ausgabe nicht das, was ich würde erwarten.

Wenn ich dieses Programm mit gcc 4.5 mit der Option "-Wall" kompiliert habe, dann gab keine Warnung.

Der Fix ist offensichtlich, den Verweis von dem Mitglied Z in dem zu entfernen Klasse Y. Allerdings ist die Klasse Y oft Teil einer Bibliothek, die ich nicht habe entwickelt (Boost :: Fusion zuletzt), und zusätzlich die Situation ist viel komplizierter als dieses Beispiel, das ich gegeben habe.

Da gibt es eine Art Option für gcc oder irgendeine zusätzliche Software, die würde mir erlauben, solche Probleme vorzugsweise zur Kompilierzeit zu erkennen, aber Selbst Laufzeit wäre besser als nichts?

Danke,

Clinton

    
Clinton 01.12.2010, 02:10
quelle

3 Antworten

2

Ich habe solche Fälle vor ein paar Monaten auf der clang-dev-Mailingliste eingereicht, aber damals hatte niemand Zeit, daran zu arbeiten (und leider auch nicht).

Argyrios Kyrtzidis arbeitet jedoch gerade daran, und hier ist sein letztes Update zu diesem Thema (30. November 23h04 GMT):

  

Ich habe das vorherige Commit viel zurückgenommen   besser in Ordnung bringen    Ссылка .   z.B. für

%Vor%
  

wir bekommen

%Vor%

Der vorherige Versuch scheiterte beim Selbst-Hosting-Test, also hoffe ich, dass dieser Versuch vergehen wird. Ich bin ziemlich froh, dass Argyrios darüber nachdenkt:)

Es ist zugegebenermaßen noch nicht perfekt, da es ein ziemlich kompliziertes Problem ist (erinnert mich irgendwie an Pointer-Aliasing), aber dies ist dennoch ein großer Schritt in die richtige Richtung.

Könnten Sie Ihren Code mit dieser Version von Clang testen? Ich bin mir ziemlich sicher, dass Argyrios das Feedback schätzen würde (ob es erkannt wurde oder nicht).

    
Matthieu M. 01.12.2010, 08:56
quelle
0

[Bearbeitete dritte Kugel, um eine Technik zu demonstrieren, die helfen kann] Dies ist das Hasenloch, das Sie verwenden, wenn eine Sprache es erlaubt, Argumente nach Wert oder Verweis mit der gleichen Aufrufersyntax zu übergeben. Sie haben folgende Möglichkeiten:

  • Ändern Sie die Argumente in nicht-konstante Referenzen. Ein temporärer Wert stimmt nicht mit einem nichtkonstanten Referenztyp überein.

  • Löschen Sie die Referenzen in Fällen, in denen dies möglich ist. Wenn Ihre const-Verweise keinen logisch geteilten Zustand zwischen Aufrufer und Aufrufer anzeigen (wenn sie dieses Problem nicht sehr häufig auftreten würden), wurden sie wahrscheinlich eingefügt, um das naive Kopieren komplexer Typen zu vermeiden. Moderne Compiler verfügen über erweiterte Optimierungsmöglichkeiten für die Kopienausgabe, die den Pass-by-Value in den meisten Fällen so effizient machen wie das Weiterleiten von Daten. Eine ausführliche Erklärung dazu finden Sie Ссылка . Copy Ellision wird eindeutig nicht ausgeführt, wenn Sie die Werte an externe Bibliotheksfunktionen übergeben, die die Provisorien ändern könnten, aber wenn dies der Fall ist, geben Sie sie entweder nicht als const-Referenzen weiter oder werfen die const absichtlich weg in der ursprünglichen Version. Dies ist meine bevorzugte Lösung, da sich der Compiler über die Kopieroptimierung Gedanken machen kann und mich über andere Fehlerquellen im Code aufklärt.

  • Wenn Ihr Compiler Rvalue-Referenzen unterstützt, verwenden Sie diese. Wenn Sie zumindest die Parametertypen der Funktionen bearbeiten können, bei denen Sie sich um dieses Problem sorgen, können Sie eine Wrapper-Metaklasse wie folgt definieren:

Vorlage & lt; Typname T & gt; Klasse need_ref {

T & amp; ref _;

öffentlich:

need_ref (T & amp; x) {/ * nichts * /}

need_ref (T & amp; x): ref_ (x) {/ * nichts * /}

Betreiber T & amp; () {return ref_; }

};

und ersetzen Sie dann Argumente des Typs T & amp; mit Argumenten vom Typ need_ref. Zum Beispiel, wenn Sie Folgendes definieren:

Klassenbenutzer {

int & amp; z;

öffentlich:

user (need_ref & lt; int & gt; arg): z (arg) {/ * nichts * /}

};

Dann können Sie ein Objekt vom Typ user mit dem Code der Form "int a = 1, b = 2; Benutzer ua (a);" sicher initialisieren, aber wenn Sie versuchen, als "Benutzersumme (a + b ) "oder" user five (5) "Ihr Compiler sollte innerhalb der ersten Version des Konstruktors need_ref () einen nicht initialisierten Referenzfehler erzeugen. Die Technik ist offensichtlich nicht auf Konstruktoren beschränkt und verursacht keinen Laufzeitaufwand.

    
spillner 01.12.2010 02:42
quelle
-1
___ qstnhdr ___ Wie erkenne ich const Referenz auf temporäre Probleme bei der Kompilierung oder Laufzeit? ___ qstntxt ___

Ich habe kürzlich festgestellt, dass die meisten Fehler in meinen C ++ Programmen sind ein Formular wie das folgende Beispiel:

%Vor%

AUSGABE: 1000

Ich würde erwarten, dass dieses Programm 123 ausgibt (der Wert von x.y.z.n wird gesetzt) get_x ()) aber die Erstellung von "Big b" überschreibt das temporäre Z. Als Ergebnis ist der Verweis auf das temporäre Z im Objekt Y jetzt mit Big b überschrieben, und daher ist die Ausgabe nicht das, was ich würde erwarten.

Wenn ich dieses Programm mit gcc 4.5 mit der Option "-Wall" kompiliert habe, dann gab keine Warnung.

Der Fix ist offensichtlich, den Verweis von dem Mitglied Z in dem zu entfernen Klasse Y. Allerdings ist die Klasse Y oft Teil einer Bibliothek, die ich nicht habe entwickelt (Boost :: Fusion zuletzt), und zusätzlich die Situation ist viel komplizierter als dieses Beispiel, das ich gegeben habe.

Da gibt es eine Art Option für gcc oder irgendeine zusätzliche Software, die würde mir erlauben, solche Probleme vorzugsweise zur Kompilierzeit zu erkennen, aber Selbst Laufzeit wäre besser als nichts?

Danke,

Clinton

    
___ tag123c ___ C ++ ist eine universelle Programmiersprache. Es wurde ursprünglich als Erweiterung von C entworfen und behält eine ähnliche Syntax, ist aber jetzt eine komplett andere Sprache. Verwenden Sie dieses Tag für Fragen zu Code, der mit einem C ++ - Compiler kompiliert werden soll. ___ tag123class ___ Eine Vorlage zum Erstellen neuer Objekte, die die allgemeinen Zustände und Verhaltensweisen beschreibt. NICHT MIT CSS-KLASSEN VERWECHSELN. Verwenden Sie stattdessen [css]. ___ answer4320745 ___

[Bearbeitete dritte Kugel, um eine Technik zu demonstrieren, die helfen kann] Dies ist das Hasenloch, das Sie verwenden, wenn eine Sprache es erlaubt, Argumente nach Wert oder Verweis mit der gleichen Aufrufersyntax zu übergeben. Sie haben folgende Möglichkeiten:

  • Ändern Sie die Argumente in nicht-konstante Referenzen. Ein temporärer Wert stimmt nicht mit einem nichtkonstanten Referenztyp überein.

  • Löschen Sie die Referenzen in Fällen, in denen dies möglich ist. Wenn Ihre const-Verweise keinen logisch geteilten Zustand zwischen Aufrufer und Aufrufer anzeigen (wenn sie dieses Problem nicht sehr häufig auftreten würden), wurden sie wahrscheinlich eingefügt, um das naive Kopieren komplexer Typen zu vermeiden. Moderne Compiler verfügen über erweiterte Optimierungsmöglichkeiten für die Kopienausgabe, die den Pass-by-Value in den meisten Fällen so effizient machen wie das Weiterleiten von Daten. Eine ausführliche Erklärung dazu finden Sie Ссылка . Copy Ellision wird eindeutig nicht ausgeführt, wenn Sie die Werte an externe Bibliotheksfunktionen übergeben, die die Provisorien ändern könnten, aber wenn dies der Fall ist, geben Sie sie entweder nicht als const-Referenzen weiter oder werfen die const absichtlich weg in der ursprünglichen Version. Dies ist meine bevorzugte Lösung, da sich der Compiler über die Kopieroptimierung Gedanken machen kann und mich über andere Fehlerquellen im Code aufklärt.

  • Wenn Ihr Compiler Rvalue-Referenzen unterstützt, verwenden Sie diese. Wenn Sie zumindest die Parametertypen der Funktionen bearbeiten können, bei denen Sie sich um dieses Problem sorgen, können Sie eine Wrapper-Metaklasse wie folgt definieren:

Vorlage & lt; Typname T & gt; Klasse need_ref {

T & amp; ref _;

öffentlich:

need_ref (T & amp; x) {/ * nichts * /}

need_ref (T & amp; x): ref_ (x) {/ * nichts * /}

Betreiber T & amp; () {return ref_; }

};

und ersetzen Sie dann Argumente des Typs T & amp; mit Argumenten vom Typ need_ref. Zum Beispiel, wenn Sie Folgendes definieren:

Klassenbenutzer {

int & amp; z;

öffentlich:

user (need_ref & lt; int & gt; arg): z (arg) {/ * nichts * /}

};

Dann können Sie ein Objekt vom Typ user mit dem Code der Form "int a = 1, b = 2; Benutzer ua (a);" sicher initialisieren, aber wenn Sie versuchen, als "Benutzersumme (a + b ) "oder" user five (5) "Ihr Compiler sollte innerhalb der ersten Version des Konstruktors need_ref () einen nicht initialisierten Referenzfehler erzeugen. Die Technik ist offensichtlich nicht auf Konstruktoren beschränkt und verursacht keinen Laufzeitaufwand.

    
___ answer4322627 ___

Ich habe solche Fälle vor ein paar Monaten auf der clang-dev-Mailingliste eingereicht, aber damals hatte niemand Zeit, daran zu arbeiten (und leider auch nicht).

Argyrios Kyrtzidis arbeitet jedoch gerade daran, und hier ist sein letztes Update zu diesem Thema (30. November 23h04 GMT):

  

Ich habe das vorherige Commit viel zurückgenommen   besser in Ordnung bringen    Ссылка .   z.B. für

%Vor%
  

wir bekommen

%Vor%

Der vorherige Versuch scheiterte beim Selbst-Hosting-Test, also hoffe ich, dass dieser Versuch vergehen wird. Ich bin ziemlich froh, dass Argyrios darüber nachdenkt:)

Es ist zugegebenermaßen noch nicht perfekt, da es ein ziemlich kompliziertes Problem ist (erinnert mich irgendwie an Pointer-Aliasing), aber dies ist dennoch ein großer Schritt in die richtige Richtung.

Könnten Sie Ihren Code mit dieser Version von Clang testen? Ich bin mir ziemlich sicher, dass Argyrios das Feedback schätzen würde (ob es erkannt wurde oder nicht).

    
___ tag123reference ___ Eine Referenz ist ein Wert, der es einem Programm ermöglicht, indirekt auf ein bestimmtes Datum wie eine Variable oder einen Datensatz im Computerspeicher oder in einem anderen Speichergerät zuzugreifen. ___ tag123temporary ___ Das Konzept, dass ein Objekt flüchtig ist, zum Beispiel temporäre Dateien oder Objekte, die nur für kurze Zeit existieren. ___ antwort4320750 ___

Das Problem hier ist der Code

%Vor%

, da das Element 'z' mit einem Verweis auf den formalen Parameter 'z' initialisiert wird. Sobald der Konstruktor zurückgegeben wird, verweist die Referenz auf ein Objekt, das nicht mehr gültig ist.

Ich glaube nicht, dass der Compiler in vielen Fällen solche logischen Fehler entdecken wird oder kann. Der Fix, dann IMO ist natürlich bewusst, solche Klassen und verwenden sie in einer ihrem Design angemessenen Weise. Dies sollte wirklich vom Bibliothekshersteller dokumentiert werden.

Übrigens ist es besser, das Element 'Y :: z' nach Möglichkeit als 'Y :: mz' zu bezeichnen. Der Ausdruck "z (z)" ist nicht sehr ansprechend

    
___
Chubsdad 01.12.2010 02:44
quelle

Tags und Links