В коде так:
#include <iostream>
#include <initializer_list>
#include <string>
struct A
{
A() { std::cout << "2" << std::endl; }
A(int a) { std::cout << "0" << std::endl; }
A(std::initializer_list<std::string> s) { std::cout << "3" << std::endl; }
A(std::initializer_list<int> l) { std::cout << "1" << std::endl; }
};
int main()
{
A a1{{}};
}
Почему это называется std::initializer_list<int>
спецификация конструктора?
Это сгенерирует ошибку компиляции неоднозначности, если мы определим, например, конструктор с std::initializer_list<double>
, Каковы правила такой конструкции и почему она так конкретна? std::initializer_list
с номером в качестве аргумента шаблона?
Если класс имеет конструктор списка инициализатора, то {whatever goes here}
значит пройти {whatevergoeshere}
в качестве аргумента для существующих конструкторов (если конструкторов списка инициализаторов нет, то whatever goes here
передаются в качестве аргументов).
Итак, давайте упростим настройку и проигнорируем другие конструкторы, потому что, видимо, компиляторы не заботятся о них
void f(std::initializer_list<std::string> s);
void f(std::initializer_list<int> l);
За f({{}})
у нас есть это правило
В противном случае, если типом параметра является std :: initializer_list и все элементы списка инициализатора могут быть неявно преобразованы в X, последовательность неявного преобразования является худшим преобразованием, необходимым для преобразования элемента списка в X, или если Список инициализаторов не имеет элементов преобразования идентичности. Это преобразование может быть пользовательским преобразованием даже в контексте вызова конструктора списка инициализаторов.
Здесь у нас есть один элемент {}
и для инициализации требуется пользовательское преобразование std::string
и нет преобразования (идентичности) для int
, Следовательно, int
выбран.
За f({{{}}})
элемент {{}}
, Может ли он быть преобразован в int
? Правило
- если список инициализаторов имеет один элемент, который сам не является списком инициализаторов, то последовательность неявного преобразования требуется для преобразования элемента в тип параметра
- …
- Во всех случаях, кроме перечисленных выше, преобразование невозможно.
Может ли он быть преобразован в std::string
? Да, потому что у него есть конструктор списка инициализатора, который имеет std::initializer_list<char> init
параметр. Следовательно, std::string
выбран на этот раз.
Разница в A a3({})
в таком случае это не инициализация списка, а «нормальная» инициализация с {}
аргумент (обратите внимание, что на одну вложенность меньше из-за отсутствия внешних скобок). Здесь наши двое f
-функции вызываются с {}
, И поскольку оба списка не имеют элементов, для обоих у нас есть преобразования идентичности и, следовательно, двусмысленность.
Компилятор в этом случае также будет учитывать f(int)
и получить связь с двумя другими функциями. Но будет применяться тай-брейк, который объявляет int
-параметр хуже чем initializer_list
параметры. Итак, у вас есть частичный заказ {int} < {initializer_list<string>, initializer_list<int>}
, что является причиной неоднозначности, так как лучшая группа последовательностей преобразования не содержит ни одного кандидата, а двух.
{}
к скалярному типу (например, int
, double
, char*
и т. д.) является преобразование личности.
{}
к типу класса, кроме специализации std::initializer_list
(например., std::string
) является пользовательским преобразованием.
Первое превосходит второе.