2014-12-10 12 views
12

Meine Frage betrifft diesen sehr einfachen und kurzen Code, bei dem eine Überladungsauflösung zwischen zwei Nicht-Template-Funktionen versucht wird, die einen Array-Referenzparameter akzeptieren. Die Frage wurde an anderer Stelle, aber in einem Vorlagenableitungskontext gepostet. Hier ist der Code:Initialiser_list als Argument für einen Array-Referenzparameter in einem Nicht-Template-Kontext

#include <iostream> 

void foo (const int (&x) [3]) { std::cout << "3\n"; } 
void foo (const int (&x) [2]) { std::cout << "2\n"; } 

int main() 
{ 
    foo({1,2,3}); 
} 

g ++ 4.8.3 kompiliert dieser Code die erste Auswahl der Funktion als (nehme ich an) die einzig gangbare, während Klirren 3.4 ist es nicht kompilieren und sagte, dass der Aufruf von foo nicht eindeutig ist (Warum?).

Welcher Compiler macht das Richtige?

clang kompiliert den Code nicht, auch wenn er die zweite Überladung entfernt: Es scheint, dass eine initializer_list einfach nicht akzeptiert wird, um eine Array-Referenz zu initialisieren.

Ist dieser Buggy?

+0

„Klirren nicht kompiliert den Code sogar das Entfernen der zweiten Überladung "- Es tut für mich (wenn die zweite Überladung entfernt wird, wie Sie sagen), mit clang 3.4.2, solange" -std = C++ 11 "in den Befehlszeilenoptionen enthalten ist . Welche Version und welcher Aufruf schlägt für Sie fehl? – hvd

+0

Sie haben Recht: nach dem Entfernen der zweiten Überladung habe ich den Code kompiliert vergessen das Flag -std = C++ 11. Das tut mir leid. – GSi

+0

Jetzt habe ich einen Befehlsalias definiert, um einen solchen Fehler in Zukunft zu vermeiden ... – GSi

Antwort

5

Ich glaube, dass, obwohl GCC Verhalten ist sinnvoller, es Verhalten klirren ist, die für C++ korrekt ist 11:

13.3.3.1.5 List-Initialisierungssequenz [over.ics.list]

2 Wenn der Parametertyp std::initializer_list<X> oder „array of X“ und alle Elemente der Initialisiererliste ist implizit in X umgewandelt werden kann, ist die implizite Umwandlungsfolge ist die schlechteste Umwandlung erforderlich, ein Element der Liste zu X konvertiert .

Diese Umwandlungssequenz beachtet die Array-Länge nicht. Beide Funktionsüberladungen geben eine implizite Konvertierungssequenz, die eine Identitätskonvertierung ist: Beide nehmen einen Verweis auf ein Array von int, und jedes Element in dem Funktionsargument ist ein int.

Überlastung Auflösung sieht dann zwei Identität Konvertierungen, und obwohl der Standard für die Lösung von Konflikten auf Conversions gleichrangig ein paar Ausnahmen hat, gibt es keine, die Aufmerksamkeit auf die Länge des Arrays zahlt:

13,3. 3.2 Rang impliziten Konvertierungssequenzen [over.ics.rank]

3 Zwei implizite Umwandlung Sequenzen von der gleichen Form sind ununterscheidbar Umwandlungsfolgen sofern nicht eine der folgenden Regeln gelten:

gefolgt von einer Liste, die Arrays überhaupt nicht erwähnt.

Jonathan Wakely weist darauf hin, dass dies sich seitdem geändert hat. Ihre Frage ist genau das, was diese Änderung ausgelöst hat, und die entsprechende DR lautet #1307. In C++ 14 ist Ihr Code gültig, aber nicht in C++ 11.

+1

Der Parametertyp ist nicht 'std :: initializer_list', und das aktuelle Arbeitspapier behandelt Arrays und' initializer_list' getrennt in verschiedenen Absätzen, speziell erwähnt die Array-Länge: _ "Andernfalls, wenn der Parametertyp" Array von NX "ist, wenn die Initialisierungsliste genau N Elemente hat oder wenn weniger als N Elemente hat und X ist standardmäßig konstruierbar, und wenn alle Elemente der Initialisierungsliste kann implizit in X konvertiert werden, die implizite Konvertierungssequenz ist die schlechteste Konvertierung, die erforderlich ist, um ein Element der Liste in X umzuwandeln. "_ –

+0

@ JonathanWakely Es heißt" 'std :: initializer_list ' oder "Array von' X' ", und der Parametertyp ist ein Array. Ich überprüfe den Wortlaut neuerer Artikel. – hvd

+3

http://open-std.org/jtc1/sc22/wg21 /docs/cwg_defects.html#1307 –

4

DR 1232 geändert C++ 11, um den Aufruf von Funktionen mit Reference-to-Array-Parametern aus einer Initialisierungsliste zu ermöglichen. (Die Änderungen am Arbeitspapier sind in N3262 gezeigt.) Alle getesteten Compiler implementieren diese Regel.

DR1307 dann die gleiche Formulierung erneut geändert, um die Mehrdeutigkeit zu beheben, die Sie entdeckt haben.Ihr Code wird von GCC- und EDG-Compilern akzeptiert, daher nehme ich an, dass Clang das DR noch nicht implementiert.

Es ist interessant zu bemerken, dass selbst nach der Auflösung von DR foo({1, 2}) immer noch mehrdeutig ist, und von allen Compilern abgelehnt. Der Grund dafür ist, dass {1, 2} zum const int(&)[2] Parameter binden kann (natürlich), aber es kann auch auf die const int(&)[3] Parameter binden, weil int default-konstruierbar ist, so bedeutet es das gleiche wie {1, 2, int()} dh {1, 2, 0}

+0

"' foo ({1, 2}) 'ist immer noch mehrdeutig" - Bist du sicher? "List-Initialisierungssequenz L1 ist keine bessere Konvertierungssequenz als die Listeninitialisierungssequenz L2, wenn ... L1 zum Typ" Array von N1 T "konvertiert, L2 konvertiert zum Typ" Array von N2 T "und N1 ist kleiner als N2. " das zugunsten der 'const int (&) [2] 'Überladung auflösen? – hvd