Ответ 1
Во-первых, правила грамматики С++ делают конечный ,
необязательный для braced-init-list. Чтобы процитировать dcl.init/1
Декларатор может указать начальное значение для идентификатора, являющегося объявлен. Идентификатор обозначает инициализацию переменной. процесс инициализации, описанный в остальной части [dcl.init] применяется также к инициализациям, указанным в других синтаксических контекстах, таких как инициализация функциональных параметров ([expr.call]) или инициализация возвращаемых значений ([stmt.return]).
initializer: brace-or-equal-initializer ( expression-list ) brace-or-equal-initializer: = initializer-clause braced-init-list initializer-clause: assignment-expression braced-init-list braced-init-list: { initializer-list ,opt } { designated-initializer-list ,opt } { }
Во-вторых, вы не можете в значительной степени переопределить систему разрешения перегрузки. Он всегда будет использовать конструктор std::initializer_list
, если вы используете такой синтаксис, и такой конструктор std::initializer_list
доступен.
Конструктор - это конструктор списка инициализаторов, если его первый параметр имеет тип std:: initializer_list или ссылка на возможно, cv-квалифицированный std:: initializer_list для некоторого типа E, и либо других параметров нет, либо все остальные параметры аргументы по умолчанию. [Примечание. Конструкторы списка инициализаторов предпочтительнее других конструкторов в инициализации списка ([over.match.list])....
Программа ниже печатает Using InitList
:
#include <iostream>
#include <initializer_list>
struct X{
X(std::initializer_list<double>){ std::cout << "Using InitList\n"; }
X(int){ std::cout << "Using Single Arg ctor\n"; }
};
int main(){
X x{5};
}
Несмотря на то, что 5
является литералом типа int
, должно было иметь смысл выбрать единственный конструктор аргументов, поскольку он идеально подходит; и конструктор std::initializer_list<double>
хочет список double
. Однако правила предпочитают std::initializer_list<double>
, потому что это его конструктор-инициализатор.
В результате даже программа ниже терпит неудачу из-за сужения конверсии:
#include <iostream>
#include <initializer_list>
struct Y{
Y(std::initializer_list<char>){ std::cout << "Y Using InitList\n"; }
Y(int, int=4){ std::cout << "Y Using Double Arg ctor\n"; }
};
int main(){
Y y1{4777};
Y y2{577,};
Y y3{57,7777};
}
В ответ на ваш комментарий ниже, "что, если нет перегрузки с std:: initializer_list, или это не первый параметр конструктора?" - тогда разрешение перегрузки не выбирает его. Демо-ролик:
#include <iostream>
#include <initializer_list>
struct Y{
Y(int, std::initializer_list<double>){ std::cout << "Y Using InitList\n"; }
Y(int, int=4){ std::cout << "Y Using Double Arg ctor\n"; }
};
int main(){
Y y1{4};
Y y2{5,};
Y y3{5,7};
}
Печать
Y Using Double Arg ctor
Y Using Double Arg ctor
Y Using Double Arg ctor
Если нет конструктора-списка инициализаторов, то инициализатор {initializer-list...,}
в значительной степени возвращается к прямой инициализации в соответствии с dcl.init/16, чья семантика охвачена следующим абзацем dcl.init/16