Var x = y nicht immer ctor?



  • Hi,

    ich dachte, Var x = y sei ein Synonym für Var x(y) und dass immer der ctor aufgerufen wird. Jedoch scheint dies nicht zu stimmen, wenn man eine Variable eines inneren Scopes mit einer gleichnamigen Variable des äußeren Scopes initialisieren möchte:

    #include <iostream>
    
    using namespace std;
    
    class Test
    {
    private:
    	int x;
    
    public:
    	Test(int x) : x(x) {}
    	int GetX() const {return x;}
    };
    
    ostream& operator<<(ostream& os, const Test& t)
    {
    	os << t.GetX();
    	return os;
    }
    
    int main()
    {
    	int z = 10;
    
    	Test test = 20;
    	cout << test;
    
    	{
    		int z = z;
    
    		std::cout << z;
    	}
    
    	std::cin.get();
    
    	return 0;
    }
    

    Das hier gibt 20 aus und dann dumpt MSVC bei int z = z mit Runtime-Check-Error, weil die uninitialisierte Variable z verwendet wird.

    Gibt es dazu mehr zu sagen, als dass es nun eben so ist? Das offensichtliche Problem ist ja, dass die Initialisierung von z nach der Definition erfolgt. Ich kann diese Nachzeitigkeit aber nicht verstehen, weil int z(z) eben das erwartete Verhalten zeigt. Trotzdem wird ja der copy-ctor aufgerufen, aber dem Scope ist das lokale z einmal bereits bekannt und einmal nicht.



  • klingt mir eher so, als würde der VC versuchn, Z mit sich selbst zu initialisieren. als nicht das äußere, sondern das innere z verwendet.



  • Ja, das ist ja auch so. Aber ich dachte, die Ausdrücke int x = y und int x(y) wären äquivalent. Wäre das so, würde er ja nicht dumpen, denn int z(z) funktioniert.



  • Eisflamme schrieb:

    Wäre das so, würde er ja nicht dumpen, denn int z(z) funktioniert.

    Zwischen

    int x = 3;
    int y = x;
    

    und

    int x = 3;
    int y (x);
    

    gibt es auch keinen Unterschied ... nicht bei int ...

    z mit sich selbst zu initialisieren ist immer 'ne blöde Idee. Egal, welche Syntax du verwendest.



  • Im Prinzip ist copy-initialisation (also T x = y ) nichts anderes als direct-initialisation (also mit Klammern), falls aber der Zieltyp eine Klasse ist, muss der Zieltyp (also T ) bei copy-initialisation aber einen copy-ctor und einen entsprechenden Konvertierungskonstruktor bereitstellen (auch wenn der nicht genutzt wird!).

    Edit: Ich bin zu spät und der TE ist Eisflamme (also, kein Anfänger). Fail.



  • Ich faile gern. Meist lern ich dabei ganz gut.

    Ich faile jetzt sogar noch weiter, denn ich verstehe es immer noch nicht. Also wieso geht Syntax A und Syntax B aber nicht. Ich will ja keine Selbstinitialisierung, es sieht nur so aus:

    Syntax A:

    int z = 10;
    {
        int z(z); // inner scope z soll mit outer scope z initialisiert werden
    } // und das hier geht so wie gewünscht!
    

    Syntax B:

    int z = 10;
    {
        int z = z; // geht nicht
    }
    

    Hm, vielleicht wurde das schon verstanden, aber die Antworten gehen ja an meiner Frage vorbei, ob es theoretisch einen Unterschied gibt. Praktisch gibt es halt keinen, daher nahm ich an, dass die Ausdrücke syntaktisch auch gleich sind. Denn wenn das = nicht als Zuweisungoperator fundiert, sondern für Constructor-Aufruf genutzt wird, hätte ich eben gedacht, es sei dasselbe. Und gleichnamige Variablen unterschiedlicher Scopes miteinander zu initialisieren ist ja per se keine schlimme Idee, das macht man bei Elementinitialisierern in Klassen doch auch:

    class Test
    {
    private:
        int x;
    
    public:
        Test(int x) : x(x) {} // valide und stilistisch in meinen Augen auch nicht verwerflich!
    };
    

  • Mod

    Ein Bezeichner gilt als deklariert am Ende der zugehörigen Deklaratorsequenz (3.3.2)

    T x(y)   ;
    //     ^ hier
    
    T x = y;
    // ^ hier
    enum x { ... };
    //    ^ hier
    
    auto foo(int x , int y(int) ) -> decltype(y(x)) ;
    //            ^x           ^y                  ^foo
    

    Für Skalare besteht kein semantischer Unterschied zwischen den verschiedenen Initialisierungsformen. In deinem Code verweisen die Initialisierer allerdings auf verschiedene Objekte.

    Da in

    int z = z;
    

    an der Stelle des Initialisierers der gerade deklarierte Bezeichner sichtbar ist, findet hier ein (undefinierte) Selbstinitialisierung statt.
    In

    class Test
    {
    private:
        int x;
    
    public:
        Test(int x) : x(x) {} // valide und stilistisch in meinen Augen auch nicht verwerflich!
    };
    

    Liegen die Verhältnisse anders: in der Memberinitialisierungsliste werden Member und Basisklassen initialisiert, es werden also von vornherein nur diese (im Scope der Klasse) gesucht, der initialisierende Ausdruck befindet sich hingen im Scope des jeweiligen Konstruktors, findet also ggf. auch Funktionsparameter.



  • Okay, dankeschöööööööööön. 🙂



  • Sone schrieb:

    Im Prinzip ist copy-initialisation (also T x = y ) nichts anderes als direct-initialisation (also mit Klammern), falls aber der Zieltyp eine Klasse ist, muss der Zieltyp (also T ) bei copy-initialisation aber einen copy-ctor und einen entsprechenden Konvertierungskonstruktor bereitstellen (auch wenn der nicht genutzt wird!).

    Edit: Ich bin zu spät und der TE ist Eisflamme (also, kein Anfänger). Fail.

    Jo, und falsch ist es auch noch. Hatten wir doch schon mal, das Thema mit dem Konvertierungskonstruktor.



  • Tachyon schrieb:

    Sone schrieb:

    Im Prinzip ist copy-initialisation (also T x = y ) nichts anderes als direct-initialisation (also mit Klammern), falls aber der Zieltyp eine Klasse ist, muss der Zieltyp (also T ) bei copy-initialisation aber einen copy-ctor und einen entsprechenden Konvertierungskonstruktor bereitstellen (auch wenn der nicht genutzt wird!).

    Edit: Ich bin zu spät und der TE ist Eisflamme (also, kein Anfänger). Fail.

    Jo, und falsch ist es auch noch. Hatten wir doch schon mal, das Thema mit dem Konvertierungskonstruktor.

    ?


Anmelden zum Antworten