Причина этого в том, что список инициализаторов здесь
do_nothing({b1, b2});
имеет тип, отличный от
std::initializer_list<B*> blist = {b1, b2};
Так как do_nothing
принимает std::initializer_list<A*>
список инициализированных фигурных скобок в вашем вызове функции (do_nothing({b1, b2})
) используется для создания std::initializer_list<A*>
из вашего параметра функции. Это работает, потому что B*
неявно конвертируется в A*
. Однако std::initializer_list<B*>
неявно не преобразуется в std::initializer_list<A*>
, поэтому вы получаете эту ошибку компилятора.
Давайте напишем некоторый псевдокод, чтобы продемонстрировать, что происходит. Сначала мы рассмотрим рабочую часть кода:
do_nothing({b1, b2}); // call the function with a braced-init-list
// pseudo code starts here
do_nothing({b1, b2}): // we enter the function, here comes our braced-init-list
std::initializer_list<A*> l {b1, b2}; // this is our function parameter that gets initialized with whatever is in that braced-init-list
... // run the actual function body
, а теперь ту, которая не работает:
std::initializer_list<B*> blist = {b1, b2}; // creates an actual initializer_list
do_nothing(blist); // call the function with the initializer_list, NOT a braced-init-list
// pseudo code starts here
do_nothing(blist): // we enter the function, here comes our initializer_list
std::initializer_list<A*> l = blist; // now we try to convert an initializer_list<B*> to an initializer_list<A*> which simply isn't possible
... // we get a compiler error saying we can't convert between initializer_list<B*> and initializer_list<A*>
Обратите внимание на условия braced-init -list и initializer_list . Хотя они выглядят одинаково, это две совершенно разные вещи.
A braced-init-list - это пара фигурных скобок со значениями между ними, что-то вроде этого:
{ 1, 2, 3, 4 }
или это:
{ 1, 3.14, "different types" }
это специальная конструкция, используемая для инициализации, которая имеет свои правила на языке C ++.
С другой стороны, std::initializer_list
это просто тип (на самом деле шаблон, но мы игнорируем этот факт, поскольку это не имеет значения). Из этого типа вы можете создать объект (как вы сделали с blist
) и инициализировать этот объект. И поскольку braced-init-list является формой инициализации, мы можем использовать ее в std::initializer_list
:
std::initializer_list<int> my_list = { 1, 2, 3, 4 };
Поскольку в C ++ есть специальное правило, которое позволяет нам инициализировать каждую функцию аргумент с компилированным списком фигурных скобок , do_nothing({b1, b2});
. Это также работает для нескольких аргументов:
void do_something(std::vector<int> vec, std::tuple<int, std::string, std::string> tup)
{
// ...
}
do_something({1, 2, 3, 4}, {10, "first", "and 2nd string"});
или вложенной инициализации:
void do_something(std::tuple<std::tuple<int, std::string>, std::tuple<int, int, int>, double> tup)
{
// ...
}
do_something({{1, "text"}, {2, 3, 4}, 3.14});