2017-06-28 12 views
0

Es scheint empirisch, dass C++ immer einen Listeninitialisierer über einen Wert Initialisierer bevorzugt. Meine Frage ist also, wie kann ich die Wertinitialisierung eines Typs erzwingen, der auch eine direkte Listeninitialisierung unterstützt. Hier ist ein minimales nicht-funktionierendes Beispiel:C++ 11 Wert Initialisierer vs Liste Initialisierer

#include <initializer_list> 

using namespace std; 

struct foo { 
    foo(int) {} 
    foo(initializer_list<char>) {} 
}; 

struct bar { 
    foo f{256}; 
}; 

In diesem Beispiel würde ich f wie initialisiert wird, um den Konstruktor foo(int) statt den foo(initializer_list<char>). Sowohl GCC als auch Clang lehnen den Code jedoch ab, weil 256 für ein Zeichen zu groß ist, was bedeutet, dass die C++ - Spezifikation die Auswahl des Listeninitialisierers erfordert. Offensichtlich beseitigt das Auskommentieren des zweiten Konstruktors von foo das Problem. Was ist der einfachste Weg, um bar zu definieren, um Wert Initialisierung auf Feld f zu verwenden? Im Idealfall könnte ich eine Kopie der Initialisierung f vermeiden, da in meinem realen Beispiel kein Kopierkonstruktor existiert.

Update

Zur Klarstellung: natürlich ist es möglich f explizit in jedem einzelnen Konstruktor bar zu initialisieren. Aber meine Frage bezieht sich speziell auf die Member-Initialisierungssyntax, da es in Situationen mit einer großen Anzahl von Konstruktoren vorzuziehen ist, bestimmte Felder an einer Stelle zu initialisieren, anstatt den Code vollständig zu kopieren.

+0

Einen Konstruktor schreiben? –

+0

Schreiben Sie einfach 'foo f (256);'. –

+0

Ich würde gerne eine Erklärung sehen, warum 'initializer_list ' ist ein praktikabler Konstruktor für '{256}'. Es ist auch nicht sicher, auf der Grundlage des Compilerverhaltens auf die Spezifikation zu schließen. Manchmal bekommen mehrere Compiler dasselbe falsch. –

Antwort

3

Solange Sie foo benötigen nicht kopierbar/beweglich zu sein, und Sie benötigen foo einen initializer_list Konstruktor zu haben, die anstelle eines Konstruktor verwendet werden könnte, ist dies das Verhalten, das Sie erhalten. Daher müssen Sie einen dieser Fakten ändern, wenn Sie dieses Problem lösen möchten.

Wenn Sie die Definition von foo überhaupt nicht ändern können, dann sind Sie geschraubt. Beschweren Sie sich, wem die Klasse gehört.

Die zweite Tatsache ist wahrscheinlich die einfachste, zu ändern, aber auch das wird nicht ohne Folgen bleiben:

struct il {}; 

struct foo { 
    foo(int) {} 
    foo(il, initializer_list<char>) {} 
}; 

Dies vollständig das Problem eindeutig machen. foo{256} ruft immer den Konstruktor mit einer ganzzahligen Ganzzahl auf. foo hat jedoch technisch keine Konstruktoren für initializer_list. Sie müssen stattdessen den Tag-Typen verwenden il es mit einer Initialisiererliste von Werten zu nennen:

foo f{il{}, {/*actual list*/}}; 

Dies erfordert verspannt mehr, aber es gibt keine wirkliche Alternative.

Beachten Sie, dass in C++ 17, garantiert elision können Sie dies tun:

struct bar { 
    foo f = foo(256); 
}; 

Unabhängig davon, ob foo mobil ist oder nicht.

+0

Interessant, dass in 'foo f = foo (256)' in C++ 17 funktioniert, auch wenn der Kopierkonstruktor von 'foo' explizit gelöscht wird. Vielen Dank. – user3188445

Verwandte Themen