2015-01-21 11 views
11

Die unten mit clang35 -std=c++11 kompilieren fehlschlägt:List-Initialisierung und Überladungsauflösung von initializer_list Konstruktor fehlgeschlagen

#include <iostream> 
#include <string> 
#include <initializer_list> 

class A 
{ 
public: 
    A(int, bool) { std::cout << __PRETTY_FUNCTION__ << std::endl; } 
    A(int, double) { std::cout << __PRETTY_FUNCTION__ << std::endl; } 
    A(std::initializer_list<int>) { std::cout << __PRETTY_FUNCTION__ << std::endl; } 
}; 

int main() 
{ 
    A a1 = {1, 1.0}; 
    return 0; 
} 

mit Fehler

init.cpp:15:14: error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing] 
    A a1 = {1, 1.0}; 
      ^~~ 
init.cpp:15:14: note: insert an explicit cast to silence this issue 
    A a1 = {1, 1.0}; 
      ^~~ 
      static_cast<int>() 

OTOH, warnt es um die Verengung und kompiliert auf g++48 -std=c++11

init.cpp: In function ‘int main()’: 
init.cpp:15:17: warning: narrowing conversion of ‘1.0e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing] 
    A a1 = {1, 1.0}; 
       ^
init.cpp:15:17: warning: narrowing conversion of ‘1.0e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing] 

und erzeugt das Ergebnis

A::A(std::initializer_list<int>) 

Sind beide Verhaltensweisen sinnvoll? Zitiert aus cppreference

All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining arguments have default values, are examined, and matched by overload resolution against a single argument of type std::initializer_list

If the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization, compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all)

Da eingrenzende Konvertierungen sind nicht erlaubt, würde ich die Überladungsauflösung Schritt erwarten, dass die A(std::initializer_list<int>) Konstruktor übereinstimmen und die stattdessen passen A(int, double) ein. Zum Beispiel stellt sowohl mit clang35 und g++48 und Druck

A::A(int, double) 

wie erwartet A(std::initializer_list<int>)-A(std::initializer_list<std::string>) ändern.

+0

Vermutlich meinst du Clang 3.5. Was Sie die Binärdatei genannt haben, ist nicht wirklich hilfreich :) 'mv clang25 clang35'" oops " –

+0

Sie haben Recht :) Dies ist die Versionskonvention, die vom Team verwendet wird, das Build-Systeme bei der Arbeit pflegt, und ich habe nie darüber nachgedacht. – Pradhan

Antwort

11

Das Verhalten ist sinnvoll. Scott Meyers hat ein Beispiel fast genau wie diese in effektiver Moderne C++ (Hervorhebung im Original):

If, however, one or more constructors declare a parameter of type std::initializer_list , calls using the braced initialization syntax strongly prefer the overloads taking std;:initializer_list s. Strongly. If there's any way for compilers to construe a call using a braced initializer to be a constructor taking a std::initializer_list , compilers will employ that interpretation.

Beispiel für die Verwendung dieser Klasse:

class Widget { 
public: 
    Widget(int, bool); 
    Widget(int, double); 
    Widget(std::initializer_list<long double>); 
}; 

Widget w1(10, true); // calls first ctor 
Widget w2{10, true}; // calls std::initializer_list ctor 
Widget w3(10, 5.0); // calls second ctor 
Widget w4{10, 5.0}; // calls std::initializer_list ctor 

Diese beiden Anrufe rufen den initializer_list Ctor obwohl sie beinhalten Umwandlung BEIDE Argumente - und obwohl die anderen Konstruktoren perfekte Übereinstimmungen sind.

Außerdem:

Compilers' determination to match braced initializers with constructors taking std::initializer_list s is so strong, it prevails even if the best-match std::initializer_list constructor can't be called. For example:

class Widget { 
public: 
    Widget(int, bool); // as before 
    Widget(int, double); // as before 
    Widget(std::initializer_list<bool>); // now bool 
}; 

Widget w{10, 5.0}; // error! requires narrowing conversions 

Beide Compiler holen die richtige Überlast (die initializer_list one) -, die wir sehen können, von der Norm gefordert wird (§13.3.1.7):

When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:

(1.1) — Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
(1.2) — If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

Aber Aufruf dieser bestimmte Konstruktor beinhaltet eine Einengung. In 8.5.1:

If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed.

So ist das Programm schlecht gebildet. In diesem Fall wählt clang einen Fehler, während gcc eine Warnung ausgibt. Beide Compiler sind konform.

+0

Welches Verhalten ist sinnvoll? clang und gcc stimmen nicht zu - clang 3.5 kann nicht kompiliert werden, während g ++ 4.8.3 die Überlastung 'initializer_list' selbst bei einer sich verengenden Konvertierung auswählt. Das obige Beispiel veranschaulicht keine der Schwierigkeiten - die Konvertierungen verengen sich nicht, und daher ist die "initializer_list" -Überladung, die ausgewählt wird, nicht überraschend. – Pradhan

+4

Wir könnten hinzufügen, dass diese _strong preference_ ist super, denn dann können Sie z.'std :: vector {" Hallo "," Welt "}' weil der Compiler _ versucht, die Initialisierungsliste als Initialisierer für Objekte vom Typ 'std :: string' zu sehen. Sonst würden wir für immer festhalten und 'std :: vector {std :: string {" Hallo "}, std :: string {" world "}}' schreiben. Und stell dir den Schmerz vor, als du versucht hast, etwas zu initiieren (http://stackoverflow.com/a/28036210). – sehe

+0

@Pradhan hinzufügen Verengung Beispiel aus dem Buch – Barry