Frage

Aus irgendeinem Grund dachte ich, C ++ 0x erlaubt std::initializer_list als Funktionsargument für Funktionen, die Typen erwarten, die zum Beispiel aus solchen konstruiert werden können std::vector. Aber anscheinend funktioniert es nicht. Ist das nur mein Compiler oder funktioniert das nie? Liegt es an potenziellen Problemen der Überlastauflösung?

#include <string>
#include <vector>

void function(std::vector<std::string> vec)
{
}

int main()
{
    // ok
    std::vector<std::string> vec {"hello", "world", "test"};

    // error: could not convert '{"hello", "world", "test"}' to 'std::vector...'
    function( {"hello", "world", "test"} );
}
War es hilfreich?

Lösung

GCC hat einen Fehler. Der Standard macht dies gültig. Sehen:

Beachten Sie, dass es zwei Seiten davon gibt

  • Wie und welche Initialisierung wird im Allgemeinen durchgeführt?
  • Wie wird die Initialisierung während der Überlastauflösung verwendet und welche Kosten haben sie?

Die erste Frage wird im Abschnitt beantwortet 8.5. Die zweite Frage wird im Abschnitt beantwortet 13.3. Zum Beispiel wird die Referenzbindung bei behandelt 8.5.3 und 13.3.3.1.4, während die Listeninitialisierung behandelt wird 8.5.4 und 13.3.3.1.5.

8.5/14,16:

Die Initialisierung, die in der Form auftritt

T x = a;

ebenso wie im Argumentier, Funktionsrückgabe, Ausnahme einer Ausnahme (15.1), der Behandlung einer Ausnahme (15.3) und der aggregierten Mitgliederinitialisierung (8.5.1) wird als Kopierinitialisierung bezeichnet.
.
.
Die Semantik der Initialisierer lautet wie folgt [...]: Wenn der Initialisierer eine verspülte Init-Liste ist, ist das Objekt eine Liste initialisiert (8.5.4).

Bei der Betrachtung des Kandidaten function, Der Compiler sieht eine Initialisiererliste (die noch keinen Typ hat - es ist nur ein grammatikalisches Konstrukt!) Als Argument und a std::vector<std::string> als Parameter von function. Um herauszufinden, was die Kosten für die Umwandlung sind und ob wir kann Konvertieren Sie diese im Zusammenhang mit Überlastung, 13.3.3.1/5 sagt

13.3.3.1.5/1:

Wenn ein Argument eine Initialisiererliste ist (8.5.4), handelt es sich nicht um einen Ausdruck, und spezielle Regeln gelten für die Konvertierung in einen Parametertyp.

13.3.3.1.5/3:

Andernfalls, wenn der Parameter eine nicht-aggregate Klasse X und eine Überlastauflösung pro 13.3.1.7 ist, wählt ein einzelner bester Konstruktor von X, um die Initialisierung eines Objekts vom Typ X aus der Argument-Initialisiererliste durchzuführen, die implizite Conversion-Sequenz ist ein Benutzer- Definierte Konversionssequenz. Benutzerdefinierte Konvertierungen sind für die Konvertierung der Initialisiererlistenelemente in die Konstruktorparametertypen zulässig, sofern nicht wie in 13.3.3.1 angegeben.

Die nicht aggregate Klasse X ist std::vector<std::string>, und ich werde den folgenden einzigen besten Konstruktor herausfinden. Die letzte Regel gewährt uns die Verwendung von Benutzern definierten Konvertierungen in Fällen wie folgt:

struct A { A(std::string); A(A const&); };
void f(A);
int main() { f({"hello"}); }

Wir dürfen die Streicher wörtlich in umwandeln std::string, Auch wenn dies eine Benutzerdefinition benötigt. Es weist jedoch auf Einschränkungen eines anderen Absatzes hin. Was macht 13.3.3.1 sagen?

13.3.3.1/4, Dies ist der Absatz, der für die Verbot mehrerer benutzerdefinierter Konvertierungen verantwortlich ist. Wir werden uns nur Listeninitialisierungen ansehen:

Bei der Betrachtung des Arguments einer von Benutzer festgelegten Konvertierungsfunktion [(oder Konstruktor)] ist dies jedoch von [...] 13.3.1.7 ein Kandidat, wenn die Initialisierungsliste als ein einziges Argument bestanden hat oder wenn die Initialisierungsliste genau ein Element hat und eine Konvertierung in eine Klasse X oder eine Bezugnahme auf (möglicherweise cv-qualifizierte) x wird für den ersten Parameter eines Konstruktors von x oder [...] berücksichtigt, sind nur Standardkonvertierungssequenzen und Ellipsis-Konvertierungssequenzen zulässig.

Beachten Sie, dass dies eine wichtige Einschränkung ist: Wenn dies nicht der Fall wäre, kann dies den oben genannten Kopierkonstruktor verwenden, um eine ebenso gute Umwandlungssequenz festzulegen, und die Initialisierung wäre mehrdeutig. (Beachten Sie die potenzielle Verwirrung von "A oder B und C" in dieser Regel: Es soll sagen "(A oder B) und C" - also sind wir eingeschränkt nur Beim Versuch, durch einen Konstruktor von X zu konvertieren, hat einen Typparameter X).

Wir sind delegiert 13.3.1.7 Zum Sammeln der Konstrukteure können wir diese Konvertierung verwenden. Nehmen wir uns diesem Absatz von der allgemeinen Seite ab beginnend von ab 8.5 die uns delegierte 8.5.4:

8.5.4/1:

Die Listeninitialisierung kann in Kontexten Direktinitialisierung oder Kopieninitialisierung auftreten. Die Listeninitialisierung in einem Kontext der Direktinitialisierung wird genannt Direktliste-Initialisierung und Listeninitialisierung in einem Kontext der Kopieinitialisierung wird genannt Initialisierung der Kopierliste.

8.5.4/2:

Ein Konstruktor ist ein Initializer-List-Konstruktor Wenn sein erster Parameter vom Typ ist std::initializer_list<E> oder Verweis auf möglicherweise CV-Quali std::initializer_list<E> Für einen Typ E und entweder gibt es keine anderen Parameter oder alle anderen Parameter haben Standardargumente (8.3.6).

8.5.4/3:

Die Listeninitialisierung eines Objekts oder einer Referenz vom Typ t wird wie folgt definiert: [...] Andernfalls werden Konstruktoren berücksichtigt, wenn t ein Klassentyp ist. Wenn T einen Initialisierer-List-Konstruktor hat, besteht die Argumentliste aus der Initialisiererliste als einzelnes Argument. Andernfalls besteht die Argumentliste aus den Elementen der Initialisiererliste. Die zutreffenden Konstruktoren werden aufgezählt (13.3.1.7) und die beste durch Überlastauflösung (13.3) ausgewählt.

In diesem Moment, T ist der Klassentyp std::vector<std::string>. Wir haben ein Argument (das noch keinen Typ hat! Wir sind nur im Zusammenhang mit einer grammatikalischen Initialisiererliste). Konstrukteure werden als von aufgezählt 13.3.1.7:

...] Wenn T einen Initializer-List-Konstruktor (8.5.4) hat, besteht die Argumentliste aus der Initialisiererliste als einzelnes Argument; Andernfalls besteht die Argumentliste aus den Elementen der Initialisiererliste. Für die Initialisierung der Kopierliste sind die Kandidatenfunktionen alle Konstrukteure von T. Wenn jedoch ein expliziter Konstruktor ausgewählt wird, ist die Initialisierung schlecht hergestellt.

Wir werden nur die Initialisiererliste von betrachten std::vector Als einziger Kandidat, da wir bereits wissen, dass die anderen nicht dagegen gewinnen oder nicht zum Argument passen. Es hat die folgende Unterschrift:

vector(initializer_list<std::string>, const Allocator& = Allocator());

Nun die Regeln für die Konvertierung einer Initialisiererliste in eine std::initializer_list<T> (Um die Kosten für die Argument/Parameterkonvertierung zu kategorisieren) werden in aufgezählte in zählern 13.3.3.1.5:

Wenn ein Argument eine Initialisiererliste ist (8.5.4), handelt es sich nicht um einen Ausdruck, und spezielle Regeln gelten für die Konvertierung in einen Parametertyp. [...] Wenn der Parametertyp ist std::initializer_list<X> und alle Elemente der Initialisiererliste können implizit in x konvertiert werden. Die implizite Konvertierungssequenz ist die schlechteste Konvertierung, die erforderlich ist, um ein Element der Liste in X umzuwandeln. Diese Konvertierung kann eine benutzerdefinierte Konvertierung sein Auch im Kontext eines Aufrufs eines Initializer-List-Konstruktors.

Jetzt wird die Initialisiererliste erfolgreich konvertiert und die Konvertierungssequenz ist eine benutzerdefinierte Konvertierung (von char const[N] zu std::string). Wie das gemacht wird, wird bei detailliert 8.5.4 wieder:

Ansonsten, wenn t eine Spezialisierung von ist std::initializer_list<E>, Ein Initializer_List -Objekt wird wie nachstehend beschrieben konstruiert und verwendet, um das Objekt gemäß den Regeln für die Initialisierung eines Objekts aus einer Klasse desselben Typs (8.5) zu initialisieren. (...)

Sehen 8.5.4/4 Wie dieser letzte Schritt gemacht wird :)

Andere Tipps

Es scheint so zu funktionieren:

function( {std::string("hello"), std::string("world"), std::string("test")} );

Vielleicht ist es ein Compiler -Fehler, aber vielleicht fragen Sie nach zu vielen impliziten Conversions.

Ich bin mir nicht sicher, aber ich vermute, dass es hier ist, dass es eine Konvertierung ist, und das Konvertieren in Vektor ist eine weitere Konvertierung. Wenn dies der Fall ist, überschreiten Sie die Grenze von nur einer impliziten Konvertierung ...

Dies ist entweder ein Compiler -Fehler oder Ihr Compiler unterstützt nicht std :: initializer_list. Getestet auf GCC 4.5.1 und es kompiliert Geldstrafe.

Sie müssen den Typ Ihrer Initializer_List angeben

function(std::initializer_list<std::string>{"hello", "world", "test"} );

Viel Glück

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top