2012-12-30 7 views
10

Der folgende Code kompiliert nicht in GCC 4.7.2 oder Clang 3.2:Kopieren einer std :: vector <std :: function <void()>> unter Verwendung einer einheitlichen Initialisierung nicht möglich. Ist das richtig?

#include <vector> 
#include <functional> 

int main() 
{ 
    std::vector<std::function<void()>> a; 
    std::vector<std::function<void()>> b{a}; 
} 

Das Problem ist, dass der Compiler eine initializer_list b verwendet wird, zu schaffen versuchen, wenn klar sollte es nur die Kopie Konstruktor aufrufen werden. Dies scheint jedoch das gewünschte Verhalten zu sein, da der Standard besagt, dass Initialisiererlisten-Konstruktoren Vorrang haben sollten.

Dieser Code würde für andere std :: vector funktionieren, aber für eine std :: -Funktion kann der Compiler nicht wissen, ob Sie den Konstruktor initializer_list oder einen anderen Konstruktor haben möchten.

Es scheint nicht, dass es einen Weg gibt, und wenn das der Fall ist, können Sie nie einheitliche Initialisierung in Vorlagencode verwenden. Was wäre eine riesige Schande.

Visual Studio (2012 November CTP) auf der anderen Seite nicht darüber beschweren. Aber die Unterstützung von initializer_list ist im Moment nicht sehr gut, also könnte es ein Fehler sein.

Antwort

10

Dies ist LWG 2132, das ist noch kein Fehlerbericht, aber es gibt klare Übereinstimmung (und Implementierungserfahrung), um es zu beheben. Der Standard besagt, dass der Konstruktor std::function jeden Typ akzeptiert. Da ein Initialisiererlistenkonstruktor anderen Konstruktoren immer vorgezogen wird, wenn er machbar ist, versucht der Code, einen Vektor aus einem std::initializer_list<std::function<void()>> mit einem einzelnen Element zu erstellen, das aus dem Objekt a initialisiert wurde. Das verursacht dann einen Fehler, denn obwohl Sie ein std::function<void()> von a erstellen können, ist das resultierende Objekt nicht aufrufbar.

Mit anderen Worten das Problem ist, dass std::function einen unbeschränkten Template-Konstruktor hat, der die Konvertierung von jedem Typ erlaubt. Das führt in Ihrem Fall zu einem Problem, da Initialisierungslisten-Konstruktoren anderen Konstruktoren vorgezogen werden, wenn sie realisierbar sind, und der unbeschränkte function-Konstruktor bedeutet, dass es immer möglich ist, einen initializer_list<function<void()>> von einem beliebigen Typ zu erstellen, sodass ein Initialisierungslistenkonstruktor immer realisierbar ist.

Die vorgeschlagene Auflösung von 2132 verhindert die Erstellung eines std::function von einem nicht aufrufbaren Typ, daher ist der Konstruktor der Initialisierungsliste nicht funktionsfähig und stattdessen wird der Kopierkonstruktor vector aufgerufen. I implemented that resolution for GCC 4.8, und es ist bereits in Clang's libC++ Bibliothek implementiert.

+0

Danke für die Antwort. Der Link LWG 2132 spricht eigentlich von einem ähnlichen, aber unterschiedlichen Problem, das gerade eben dieses Problem behebt. Das heißt, das Problem wird nur für std :: function behoben, nicht für andere Typen mit Vorlagenkonstruktoren. Ich denke, dass meine neue Regel für die uniforme Initialisierung lautet: "Verwenden Sie es, wo immer Sie können, außer wenn das Objekt einen Initializer_list -Konstruktor hat. Verwenden Sie es dann nur, wenn Sie diesen Konstruktor wollen."Das bedeutet auch, dass Sie es nicht in Vorlagencode verwenden können. –

+0

Ich bevorzuge die Regel" schreiben Sie nicht bedingte Konstruktorvorlagen, die implizite Konvertierung von jedem Typ erlauben. "Wenn Sie solche Typen nicht erstellen, dann haben Sie gewonnen." Probleme für Leute, die versuchen, Ihren Typ mit Klassen zu verwenden, die Initialisierungslisten-Konstruktoren haben Die Standardbibliothek folgte dieser Regel nicht, aber das wurde für 'std :: function' korrigiert. –

5

Ich kann keinen Grund sehen, warum dies nicht kompilieren sollte und beide gcc (Version 4.8.0 20121111) und clang (Version 3.3 (trunk 171007)) kompilieren den Code. Das heißt, "uniform initialization" ist bei weitem nicht einheitlich: Es gibt Fälle, in denen Sie beim Aufruf eines Konstruktors keine geschweiften Klammern verwenden können.

+0

g ++ 4.7.2 ('4.7.2-5ubuntu1') kompiliert den Code nicht. Sehr seltsame Compiler-Fehlermeldung: http://pastebin.com/b1mcbYRq – leemes

+1

@leemes da die Frage mit "Der folgende Code nicht kompiliert in GCC 4.7.2 oder Clang 3.2:", gehe ich davon aus, dass das OP weiß dies (und ist wahrscheinlich der Grund für die Frage an erster Stelle). – WhozCraig

+0

@WhozCraig Ja, aber er hat die Compiler-Nachricht nicht zur Verfügung gestellt. Aus diesem Grund habe ich diesen Kommentar an erster Stelle veröffentlicht. – leemes