Завершающая запятая в равномерной инициализации
Есть ли потенциальная семантическая разница, когда я использую запятую во время равномерной инициализации?
std::vector< std::size_t > v1{5, }; // allowed syntax
std::vector< std::size_t > v2{10};
Можно ли использовать запятую, чтобы компилятор выбирал std::vector::vector(std::initializer_list< std::size_t >)
конструктор вместо std::vector::vector(std::size_t, const std::size_t &)
или есть другие хитрости с упомянутым синтаксисом?
Могу ли я использовать его, чтобы обнаружить, есть ли std::initializer_list
перегрузка
Учитывая следующий код, какой конструктор должен быть выбран?
struct A { A(int) { ; } A(double, int = 3) { ; } };
A a{1};
A b{2, };
Этот код принятgcc 8
а также A(int)
выбран в обоих случаях.
2 ответа
Во-первых, правила грамматики C++ делают конечный ,
необязательно для 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.
Нет. Эта запятая является концом, чтобы заставить трюки макроса препроцессора работать без ошибок компиляции. Это ничего не значит о вашем типе данных или его размере.