GCC <--> VS unterschiedliches Verhalten



  • Hallo!
    Langsam nervt mich der Visual Studio (2013)-Compiler, da er Code anders bzw. garnicht kompiliert, was im GCC jedoch wie erwartet funktioniert.

    Fall 1:

    #include <array>
    #include <iostream>
    
    struct A {
    	int member;
    
    	A() 
    		: member(0) {
    		std::cout << "A()\n";
    	}
    
    	A(int m) 
    		: member(m) {
    		std::cout << "A(int)\n";
    	}
    
    	A(const A& x) 
    		: member(x.member) {
    		std::cout << "A(const A&)\n";
    	}
    
    	A(A&& x)
    		: member(x.member) { 
    		std::cout << "A(A&&)\n"; 
    	}
    };
    
    class B {
    	std::array<A, 3> arr;
    
    public:
    	B() : arr{ { 1, 2, 3 } } { }
    };
    
    int main() {
    	B b;
    }
    

    Die GCC-Ausgabe:

    A(int)
    A(int)
    A(int)
    

    VS-Ausgabe (im optimierten Release-Mode):

    A(int)
    A(int)
    A(int)
    A(A&&)
    A(A&&)
    A(A&&)
    

    Was soll der Quark? Wieso initialisiert VS das Array, indem es erst mal aus den Literalen temporäre Objekte konstruiert, um sie gleich wieder zu moven, anstatt wie GCC es einfach lässt und das Array direkt mit den Konstruktoren aus den Literalen initialisiert? Im Debug-Modus ist es mit VS sogar noch schlimmer, da nimmt es die Copy-Konstruktoren.

    Fall 2:
    Verschiebe die Definition von A in B's private-Bereich:

    #include <array>
    #include <iostream>
    
    class B {
    	struct A {
    		int member;
    
    		A()
    			: member(0) {
    			std::cout << "A()\n";
    		}
    
    		A(int m)
    			: member(m) {
    			std::cout << "A(int)\n";
    		}
    
    		A(const A& x)
    			: member(x.member) {
    			std::cout << "A(const A&)\n";
    		}
    
    		A(A&& x)
    			: member(x.member) {
    			std::cout << "A(A&&)\n";
    		}
    	};
    
    	std::array<A, 3> arr;
    
    public:
    	B() : arr{ { 1, 2, 3 } } { }
    };
    
    int main() {
    	B b;
    }
    

    VS liefert:

    error C2248: 'B::A' : cannot access private struct declared in class 'B'
    ...
    This diagnostic occurred in the compiler generated function 'std::array<B::A,3>::array(std::array<B::A,3> &&)'
    

    Was macht VS denn da? Etwa implizit ein temporäres std::array<B::A,3> aus { 1, 2, 3 }, um es dann nach arr zu moven?



  • Die Fehlermeldung macht echt keinen Sinn. Sobal der Name "A" bekannt ist, darf er auch verwendet werden. Zum Rest: Darf man so machen, auch wenns nicht sinnvoll ist...



  • msvcbashe54r schrieb:

    Die Fehlermeldung macht echt keinen Sinn. Sobal der Name "A" bekannt ist, darf er auch verwendet werden. Zum Rest: Darf man so machen, auch wenns nicht sinnvoll ist...

    Darf man? Was, wenn der Typ, den ich speichern will, nicht moveable ist (indem er z.B. ein std::mutex enthält oder so).

    Außerdem finde ich es arg bedenklich, dass Visual Studio in Release-Modus ganz anderen Code ausführt als im Debug-Modus (wenn er einfach weniger bzw. logisch äquivalenten Code ausführen würde aus Optimierungen, RVO etc. wäre es ja okay, aber einen gänzlich anderen Konstruktor zu benutzen finde ich arg merkwürdig. Das macht den Debug- als Test-Modus ziemlich nutzlos.

    Die Fehlermeldung ist nur wie es aussieht kein Bug der STL-Implementierung sondern vom Compiler. Schön zu sehen, dass RValue-Semantik auch in VS 2013 immernoch nicht wirklich funktioniert 😡 .


  • Mod

    Was soll der Quark? Wieso initialisiert VS das Array, indem es erst mal aus den Literalen temporäre Objekte konstruiert, um sie gleich wieder zu moven, anstatt wie GCC es einfach lässt und das Array direkt mit den Konstruktoren aus den Literalen initialisiert

    VS ist hier nicht standardkonform. Der Standard schreibt hier keine Temporary vor, es handelt sich also nicht um irgendeine Elidierung vom GCC.

    When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. [...]
    [ Note: If an initializer-clause is itself an initializer list, the member is list-initialized, which will result in a recursive application of the rules in this section if the member is an aggregate. — end note ]

    Sprich, dass interne, echte Array von std::array wird mit { 1, 2, 3 } initialisiert. Und das wiederum heißt, dass jedes Arrayelement mit der dazugehörigen Zahl initialisiert wird. Da entsteht nirgends eine Temporary.


Anmelden zum Antworten