Versteckt den Namen der int-Variablen in C ++

7

Aus Neugier habe ich diesen Code aus einer Interviewfrage [*]

probiert %Vor%

Bei der Kompilierung unter Linux (sowohl g ++ 4.6.3 als auch clang ++ 3.0) wird Folgendes ausgegeben:

%Vor%

Unter Windows (VS2010) wird jedoch Folgendes ausgegeben:

%Vor%

Das Grundprinzip wäre, dass, bis der Copy-Konstruktor der zweiten 'a' Variablen beendet ist, die erste 'a' Variable noch zugänglich ist. Ich bin mir jedoch nicht sicher, ob das Standardverhalten ist oder nur eine (andere) Microsoft-Eigenart.

Irgendeine Idee?

[*] Die eigentliche Frage war:

  

Wie würden Sie eine Variable innerhalb eines Bereichs mit dem Wert einer identisch benannten Variablen im enthaltenden Bereich initialisieren, ohne eine temporäre oder globale Variable zu verwenden?

%Vor%     
rippeltippel 23.05.2012, 16:11
quelle

5 Antworten

0

Ich habe mir den Standard angesehen, es ist eigentlich eine Grauzone, aber hier sind meine 2 Cent ...

  

3.1 Erklärungen und Definitionen [basic.def]

     
  1. Eine Deklaration führt Namen in eine Übersetzungseinheit ein oder deklariert Namen, die von früheren Deklarationen eingeführt wurden.

  2.   
  3. Eine Deklaration ist eine Definition, es sei denn ... [nicht relevante Fälle folgen]

  4.   

3.3.1 Deklarationspunkt

     
  1. Der Deklarationspunkt für einen Namen ist unmittelbar nach seinem vollständigen Deklarator und vor seinem Initialisierer (falls vorhanden), außer wie unten angegeben [Beispiel für die Selbstzuweisung].

  2.   
  3. Ein nichtlokaler Name bleibt bis zur Deklaration des lokalen Namens sichtbar, der ihn versteckt.

  4.   

Wenn wir jetzt davon ausgehen, dass dies der Punkt der Erklärung des inneren 'a' (3.3.1 / 1) ist

%Vor%

dann sollte das äußere 'a' bis zu diesem Punkt (3.3.1 / 2) sichtbar sein, wo das innere 'a' definiert ist.

Problem ist, dass in diesem Fall laut 3.1 / 2 eine Deklaration eine Definition ist. Dies bedeutet, dass das innere "a" geschaffen werden sollte. Bis dahin kann ich aus dem Standard nicht verstehen, ob das äußere 'a' noch sichtbar ist oder nicht. VS2010 geht davon aus, dass dies der Fall ist, und alles, was in die Klammern fällt, bezieht sich auf den äußeren Bereich. Allerdings behandeln clang ++ und g ++ diese Zeile als einen Fall der Selbstzuweisung, was zu undefiniertem Verhalten führt.

Ich bin nicht sicher, welcher Ansatz richtig ist, aber ich finde VS2010 konsistenter: der äußere Bereich ist immer noch sichtbar, bis das innere 'a' vollständig erstellt ist.

    
rippeltippel 24.05.2012, 09:24
quelle
10
  

Wie würden Sie eine Variable innerhalb eines Bereichs mit dem Wert einer identisch benannten Variablen im enthaltenden Bereich initialisieren, ohne eine temporäre oder globale Variable zu verwenden?

Wenn der äußere Bereich nicht explizit benannt werden kann, können Sie dies nicht tun. Sie können den globalen Bereich, die Namespacebereiche und die Klassenbereiche explizit benennen, jedoch weder Anweisungs- noch Anweisungsbereiche.

C ++ 11 [basic.scope.pdecl 3.3.2 p1 gibt an:

  

Der Punkt der Deklaration für einen Namen ist unmittelbar nach seinem vollständigen Deklarator (Abschnitt 8) und vor seinem Initialisierer (falls vorhanden), außer wie unten angegeben. [ Beispiel:

%Vor%      

Hier wird das zweite x mit einem eigenen (unbestimmten) Wert initialisiert. -end Beispiel ]

MSVC implementiert dieses Beispiel ordnungsgemäß, implementiert es jedoch nicht ordnungsgemäß, wenn der Initialisierer Klammern anstelle von Zuordnungssyntax verwendet. Es gibt a Fehler über Microsoft Connect.

Hier ist ein Beispielprogramm mit falschem Verhalten in VS als Folge dieses Fehlers.

%Vor%

C ++ enthält den folgenden Hinweis.

  

[ Hinweis: Operationen mit unbestimmten Werten können zu undefiniertem Verhalten führen. -Ende beachten ]

Dieser Hinweis weist jedoch darauf hin, dass Vorgänge zu undefiniertem Verhalten führen können, nicht notwendigerweise zu . Der oben verlinkte Fehlerbericht enthält eine Bestätigung von Microsoft, dass dies ein Fehler ist und nicht, dass das Programm undefiniertes Verhalten auslöst.

Bearbeiten: Und jetzt habe ich das Beispiel geändert, so dass das Objekt mit unbestimmtem Wert nur in einem nicht bewerteten Kontext "benutzt" wird, und ich glaube, dass dies absolut die Möglichkeit von undefiniertem Verhalten ausschließt auf irgendeiner Plattform, während der Fehler in Visual Studio immer noch demonstriert wird.

    
bames53 23.05.2012 16:28
quelle
6
  

Wie würden Sie eine Variable innerhalb eines Bereichs mit dem Wert einer identisch benannten Variablen im enthaltenden Bereich initialisieren, ohne eine temporäre oder globale Variable zu verwenden?

Wenn Sie den Wortlaut technisch verstehen wollen, ist das ziemlich einfach. Ein "temporäres" hat eine spezifische Bedeutung in C ++ (siehe §12.2); Jede benannte Variable, die Sie erstellen, ist keine temporäre Variable. Daher können Sie einfach eine lokale Variable (die nicht eine temporäre ist) mit dem korrekten Wert initialisieren:

%Vor%

Eine noch vertretbarere Möglichkeit wäre die Verwendung eines Verweises auf die Variable im äußeren Bereich:

%Vor%

Dadurch wird überhaupt keine zusätzliche Variable erstellt - es wird lediglich ein Alias ​​für die Variable im äußeren Bereich erstellt. Da der Alias ​​einen anderen Namen hat, behalten wir den Zugriff auf die Variable im äußeren Bereich bei, ohne dafür eine Variable (temporär oder nicht) zu definieren. Viele Verweise werden intern als Zeiger implementiert, aber in diesem Fall (zumindest mit einem modernen Compiler und optimierter Optimierung) würde ich erwarten, dass es nicht so ist - dass der Alias ​​wirklich nur als ein anderer Name behandelt würde, der sich auf die Variable bezieht im äußeren Bereich (und ein schneller Test mit VC ++ zeigt, dass es so funktioniert - die generierte Assemblersprache verwendet ref_a überhaupt nicht).

Eine andere Möglichkeit in der gleichen Richtung wäre wie folgt:

%Vor%

Das ist etwas ähnlich wie die Referenz, nur dass es in diesem Fall nicht einmal Platz für ein Argument gibt, ob a_val eine Variable heißen könnte - es ist absolut nicht eine Variable. Das Problem ist, dass eine Enumeration nur mit einem konstanten Ausdruck initialisiert werden kann. Daher müssen wir die äußere Variable als const definieren, damit sie funktioniert.

Ich bezweifle, dass das der Interviewer wirklich beabsichtigt hat, aber alle beantworten die Frage wie gesagt. Die erste ist (zugegebenermaßen) eine reine Formalität bezüglich der Begriffsdefinitionen. Die zweite könnte noch offen für einige Argumente sein (viele Leute denken über Referenzen als Variablen). Obwohl es den Rahmen einschränkt, gibt es keinen Raum für Fragen oder Diskussionen über das dritte.

    
Jerry Coffin 23.05.2012 17:44
quelle
2

Was Sie tun, indem Sie eine Variable mit sich selbst initialisieren, ist undefiniertes Verhalten. Alle deine Testfälle haben es richtig gemacht, das ist keine Eigenart. Eine Implementierung könnte auch a auf 123456789 initialisieren und wäre immer noch Standard.

Update: Die Kommentare zu dieser Antwort zeigen, dass das Initialisieren einer Variablen mit sich selbst kein undefiniertes Verhalten ist, sondern dass versucht wird, eine solche Variable zu lesen.

    
K-ballo 23.05.2012 16:14
quelle
1
  

Wie würden Sie eine Variable innerhalb eines Bereichs mit dem Wert einer identisch benannten Variablen im enthaltenden Bereich initialisieren, ohne eine temporäre oder globale Variable zu verwenden?

Sie können nicht. Sobald der identische Name deklariert ist, ist der äußere Name für den Rest des Bereichs nicht zugänglich. Sie benötigen eine Kopie oder einen Alias ​​der äußeren Variablen, was bedeutet, dass Sie eine temporäre Variable benötigen.

Ich bin überrascht, dass VC ++, selbst wenn die Warnstufe angekurbelt ist, sich nicht über diese Zeile beschweren wird:

%Vor%

Visual C ++ warnt Sie manchmal davor, eine Variable zu verbergen (vielleicht nur für Mitglieder abgeleiteter Klassen). Es ist auch normalerweise gut, Ihnen mitzuteilen, dass Sie einen Wert verwenden, bevor er initialisiert wurde, was hier der Fall ist.

Wenn man sich den generierten Code anschaut, passiert es, dass das innere a auf denselben Wert des äußeren a initialisiert wird, weil das in einem Register zurückbleibt.

    
Adrian McCarthy 23.05.2012 16:49
quelle