Seite 1 von 1

[C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.01.2013, 13:05
von Schrompf
Moin mal wieder,

ich arbeite oft mit kleinen Helferstrukturen wie z.B.

Code: Alles auswählen

struct Dings { Objekt* obj; float wert; size_t zahl; }
Wenn ich nun Instanzen dieser Helferstruktur anlegen will, muss ich entweder von Hand einen Konstruktor schreiben, der jedes einzelne Element als Parameter nimmt und durchreicht, oder ich benutze die von C bekannte Init-Liste:

Code: Alles auswählen

 Dings d = { obj, wert, zahl };
Nur leider kann ich diese Konstruktion nun nicht innerhalb einer Zeile anwenden. All das folgende geht nicht:

Code: Alles auswählen

vector.push_back( Dings( obj, wert, zahl));
vector.push_back( Dings{ obj, wert, zahl });
vector.push_back( Dings d = { obj, wert, zahl } );
Gibt es einen schicken C++11-Syntax, wie ich in einer Zeile eine Instanz einer Struktur anlegen und elementweise initialisieren kann?

[Edit]Zur Sicherheit: der Compiler ist VS2012 SP1 CTP Nov12, der müsste Initializer Lists können. Behauptet zumindest Microsoft

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.01.2013, 13:18
von CodingCat
Folgende Varianten sind gültig:

Code: Alles auswählen

#include <vector>
#include <cstddef>
 
class Objekt;
struct Dings { Objekt* obj; float wert; size_t zahl; };
 
int main()
{
    std::vector<Dings> dings;
    dings.push_back( Dings{ nullptr, 7.0f, 4 } );
    dings.push_back( { nullptr, 7.0f, 4} );
    return 0;
}
Würde mich nicht wundern, wenn die hirnlosen Idioten bei MS das Wichtigste mal wieder vergessen hätten.

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.01.2013, 13:37
von Schrompf
So scheint es, ja. Erstere Version ergibt:

Code: Alles auswählen

error C2275: 'Dings' : illegal use of this type as an expression
error C2143: syntax error : missing ')' before '{'
error C2143: syntax error : missing ';' before '{'
error C2143: syntax error : missing ';' before '}'
error C2059: syntax error : ')'
Zweiteres ohne explizite Angabe des Typs ergibt:

Code: Alles auswählen

error C2664: 'void std::vector<Dings,std::allocator<_Ty>>::push_back(const Dings &)' : cannot convert parameter 2 from 'initializer-list' to 'Dings &&'
with
[
   _Ty=Dings
]
Reason: cannot convert from 'initializer-list' to 'Dings'
Only an initializer-list with zero or one elements can be converted to this type
Letzteres ist evtl. auch nur der Tatsache geschuldet, dass wie von MS berichtet die Standard-Lib noch nicht auf dem aktuellen Stand ist. Ersteres sollte aber einen Konstruktor-Aufruf auslösen, müsste also ein Sprach-Feature und kein Feature der Standardlib sein. Und demzufolge nach meinem Verständnis funktionieren. Schade.

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.01.2013, 13:45
von CodingCat
Beide sollten zu einem Konstruktoraufruf aufgelöst werden, unabhängig von der STL. :-/

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.01.2013, 13:56
von dot
Ich denk in dem Zusammenhang sollte man auch std::vector::emplace_back erwähnen... ;)

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.01.2013, 14:10
von CodingCat
dot hat geschrieben:Ich denk in dem Zusammenhang sollte man auch std::vector::emplace_back erwähnen... ;)
Könnte man, allerdings müsste man für emplace_back() wieder mindestens einen Konstruktor schreiben. Dabei ist es SO Zeit, dass diese ewige nutzlose Konstruktorschreiberei endlich aufhört.

Nebenbei kann ich mit emplace_back() noch immer nix anfangen, weil ich überhaupt nicht sehe, welche Parameter erwartet werden. Ich habe das ungute Gefühl, dass in C++11 mit Variadic Templates schon wieder zu minimalistisch und allgemein gegriffen hat. Aber möglicherweise schaffen IDEs in 5-10 Jahren ja sogar eine Propagierung der Parameternamen und -typen. Einfacher wird es jedenfalls nicht.

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.01.2013, 19:53
von dot
CodingCat hat geschrieben:Könnte man, allerdings müsste man für emplace_back() wieder mindestens einen Konstruktor schreiben. Dabei ist es SO Zeit, dass diese ewige nutzlose Konstruktorschreiberei endlich aufhört.
Das ist richtig, aber ein fehlender Konstruktor ist ja wohl auch das Problem bei den Varianten mit der Initializer List. Dort wird eben überall ein Temporary erzeugt und der Compiler wählt richtig die Variante von push_back aus, die eine rvalue Reference als Parameter nimmt. Am Ende scheitert es wieder mal daran, dass MSVC noch keine impliziten Move Konstruktoren hat...

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.01.2013, 19:57
von CodingCat
dot hat geschrieben:Das ist richtig, aber um ein fehlender Konstruktor ist ja wohl auch das Problem bei den Varianten mit der Initializer List. Dort wird eben überall ein Temporary erzeugt und der Compiler wählt richtig die Variante von push_back aus, die eine rvalue Reference als Parameter nimmt. Am Ende scheitert es wieder mal daran, dass MSVC noch keine impliziten Move Konstruktoren hat...
Nein, es scheitert daran, dass MSVC offenbar noch immer keine Initializer-List-Konstruktion kann, sofern kein explizit definierter Initializer-List-Konstruktor vorhanden ist. Dass in diesem Fall mangels impliziter Move-Konstruktoren obendrein nicht verschoben würde, ist richtig, würde jedoch einfach in einer Kopie resultieren, nicht in einem Fehler.

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.01.2013, 19:59
von dot
Stimmt, die Fehlermeldung sagt eigentlich auch genau das...

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 24.09.2014, 14:30
von Schrompf
Ich hänge übrigens jetzt, anderthalb Jahre später, mit VSVC2013 wieder an dem selben Ärgernis. Anscheinend kann VisualStudio immernoch keine anonymen Instanzen per Uniform Initialization anlegen.

Ich so:

Code: Alles auswählen

sammlung.push_back( MeineStruktur{ a, b, c });
Und darauf er so:
EffingCompiler hat geschrieben:1>c:\projekte\knuiff.cpp(55): error C2143: Syntaxfehler: Es fehlt ')' vor '{'
1>c:\projekte\knuiff.cpp(55): error C2059: Syntaxfehler: ')'
1>c:\projekte\knuiff.cpp(55): error C2143: Syntaxfehler: Es fehlt ';' vor '{'
1>c:\projekte\knuiff.cpp(55): error C2143: Syntaxfehler: Es fehlt ';' vor '}'

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 24.09.2014, 14:50
von Schrompf
Ok, das war evtl. wieder ein Eigentor. Ich kompiliere ja für verdammte XP-Kompatibilität immernoch mit VC2012-XP-Compiler. Evtl. kann das VC2013 doch schon, ich merk's nur nicht.

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 24.09.2014, 15:06
von dot
Schrompf hat geschrieben:Evtl. kann das VC2013 doch schon, ich merk's nur nicht.
Jop, in VS 2013 geht's :)

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 24.09.2014, 16:04
von Spiele Programmierer
Ich benutze ja zur Zeit tatsächlich hauptsächlich eine 2013er CTP Version.
Bei mir steht aber auch "Visual Studio 2013 - Windows XP(v120_xp)" zur Auswahl.

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 24.09.2014, 16:06
von Schrompf
Auch grade festgestellt. Und wenn ich wirklich mächtig Glück habe, reicht es für XP-startfähige Exen aus, das exe-erzeugende Projekt mit dem XP-Compiler zu bauen und die umfangreiche Kette an Libs vorneweg kann alles an C++11-Support nutzen, was Microsoft mir hingeworfen hat.

@v120_xp: jupp, das wär auch ne Variante. Ich habe allerdings meinen Nachmittag mit dem Versuch verschwendet, Boost mit diesem Compiler neuzubauen. Und das ist notwendig, sonst beschwert sich der Linker nachher, dass er v110_xp-gebaute Libs nicht linken kann. Keine Ahnung, wie ich Boost damals für v110_xp gebaut bekommen habe...

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 24.09.2014, 17:25
von Krishty
Leicht off-topic: Wie sind denn deine Erfahrungen mit der 2012er CRT? Ist die breit verfügbar?

Ich benutze noch 100_xp weil ich vor allem nicht möchte, dass die Anwender die Laufzeitbibliothek installieren müssen. Fast jeder XP-Nutzer da draußen hat die Visual C++-2010-CRT installiert, weil sie halt seit fünf Jahren von allen möglichen Tools und Treibern vorausgesetzt wird. Die 2012er/13er CRT bedeutet einen zusätzlichen Download; eine zusätzliche Installation; dabei dann Probleme weil 10 Jahre alte XP-Installationen ihren Windows Installer-Dienst zerschossen haben und Opa seinen Enkel nicht erreichen kann, und der dann sowieso erstmal Vista Windows 7 Windows 8 Windows 8.1 Windows 9 installieren würde.

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 24.09.2014, 17:37
von Schrompf
Da habe ich keine sinnvollen Statistiken für Dich, nur gefühltes Wissen. Und das lautet: bei Desura hat man erst auf mein Bestreben überhaupt die VS2013-Runtime in die Dependencies aufgenommen. Bei Steam (ein Jahr später) war sie aber ab Start verfügbar. Und da heutzutage praktisch niemand mehr noch Installer runterlädt, ist für mich das Thema damit eigentlich gegessen.

Kannst Du nicht die Runtime statisch linken? Das würde ich ja tun, weil mir Exe-Größen egal sind. Leider müssen dann alle Libs, die man so ansaugt, auch statisch gegen die Runtime linken. Und bei QT ist das quasi unmöglich, als ich es das letzte Mal probiert habe, weswegen ich irgendwann zähneknirschend hingenommen habe, dynamisch zu linken.

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 24.09.2014, 17:53
von Krishty
Statisches Einbinden macht bei mir die Ausnahmebehandlung kaputt, weil ich die erst zur Hälfte selber implementiert habe :( Wenn ich so weit bin, dass statisches Linken klappt, brauche ich die CRT sowieso nicht mehr. Ist nur leider großer Brainfuck, da hinzukommen.

Aber jetzt wo du mich drauf gebracht hast, sollte ich wieder damit anfangen. Letzten Endes würden wohl alle meine Projekte profitieren. Andererseits könnte ich auch einfach auf Ausnahmen verzichten; das würde die Sache noch einfacher machen.

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.09.2014, 12:13
von snoob741
Hast Du mal geprüft ob der VC mit Lambda expressions klar kommt? Dann könntest Du auch Einzeiler
in der Form nutzen:

Code: Alles auswählen

	dings.push_back([]()-> Dings { return Dings{nullptr, 7.0f, 4}; }());
Bzw. wenn Du deinem Stuct noch einen Konstruktor verpasst, könntest Du den Lambda expression
auch als delegate factory nutzen:

Code: Alles auswählen

        class Objekt;
	struct Dings 
	{
		Dings(Objekt* o, float w, size_t z):obj(o),wert(w), zahl(z){}
		Objekt* obj; float wert; size_t zahl; 
	};
	
	auto d = [](Objekt* obj, float wert, size_t zahl)-> Dings { return Dings(obj, wert, zahl);};
	
	std::vector<Dings> dings;	
	dings.push_back(d(nullptr, 7.0f, 4));
        dings.push_back(d(nullptr, 0.25f, 1));

Re: [C++] initializer_list innerhalb einer Zeile benutzen

Verfasst: 25.09.2014, 13:18
von Schrompf
Die Ideen sind interessant - ich bin immmer wieder erstaunt, wieviele Wege es in C++ gibt, das selbe Ziel zu erreichen. Die Ideen sind aber leider unpraktisch - mir geht es ja nur darum, etwas Tipparbeit zu sparen. Und wenn ich der Klasse einen Konstruktor gebe, kann ich den auch gleich direkt benutzen:

Code: Alles auswählen

struct Dings 
{
Dings(Objekt* o, float w, size_t z):obj(o),wert(w), zahl(z){}
Objekt* obj; float wert; size_t zahl; 
};

std::vector<Dings> dings;	
dings.push_back(Dings(nullptr, 7.0f, 4));
Ich hatte es dann mit leisem Grummeln in einer Extrazeile gelöst, was ja ganz klassisches C ist:

Code: Alles auswählen

Dings d = { nullptr, 7.0f, 4 };
container.push_back( d);