2015-01-21 4 views
11

Ниже не может скомпилировать с clang35 -std=c++11:Список инициализация и не разрешение перегрузки initializer_list конструктора

#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; 
} 

с ошибкой

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>() 

Ото, он предупреждает о сужении и компилирует на 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] 

и производит результат

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

Имеет ли смысл какое-либо поведение? Цитируя 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)

Поскольку сужение преобразования не допускаются, я бы ожидать, что шаг разрешение перегрузки не совпадать с A(std::initializer_list<int>) конструктора и вместо того, чтобы соответствовать A(int, double) один. Например, изменение A(std::initializer_list<int>) в A(std::initializer_list<std::string>) компилируется как clang35 и g++48 и печатает

A::A(int, double) 

, как и ожидалось.

+0

Предположительно, вы имеете в виду Clang 3.5. То, что вы назвали двоичным, не очень полезно :) 'mv clang25 clang35'" oops " –

+0

Вы правы :) Это соглашение об использовании версий, используемое командой, поддерживающей системы сборки на работе, и я никогда не думал об этом. – Pradhan

ответ

11

Поведение имеет смысл. Скотт Мейерс есть пример почти так же, как это в современном эффективном C++ (курсив в оригинале):

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.

Пример использование этого класса:

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 

Этих два вызова вызывает initializer_list т е р, даже если они включают преобразование Оба аргумента - и даже если другие конструкторы являются идеальными совпадениями.

Кроме того:

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 

Оба составителей выбрать правильную перегрузку (initializer_list один) - который мы можем видеть, что требуется от стандарта (§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.

Но вызов этот конкретный конструктор предполагает сужение. В 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.

Таким образом, программа плохо сформирована. В этом случае clang выбирает ошибку, а gcc выбирает предупреждение. Оба компилятора соответствуют.

+0

Какое поведение имеет смысл? clang и gcc disagree - clang 3.5 не удается скомпилировать, тогда как g ++ 4.8.3 выбирает перегрузку 'initializer_list' даже при сужении конверсии. Приведенный выше пример не иллюстрирует никаких трудностей - конверсии не сужаются и, следовательно, перегрузка перегрузки «initilalizer_list» не удивительна. – Pradhan

+4

Мы могли бы добавить, что это _strong preference_ является удивительным, потому что тогда вы можете, например,'std :: vector {" hello "," world "}', потому что компилятор _makes the force_ пытается просмотреть список инициализаторов как инициализаторы для объектов типа 'std :: string'. В противном случае мы бы навсегда застряли в записи 'std :: vector {std :: string {" hello "}, std :: string {" world "}}'. _ И представьте себе боль, когда вы пытались инициализировать что-то [менее тривиальное] (http://stackoverflow.com/a/28036210) _. – sehe

+0

@Pradhan добавляет пример сужения из книги – Barry