Это не вписывается в комментарий, поэтому я публикую его как ответ. То, что вы называете основной структурой , вовсе не является структурой (или типом), а формой при инициализации. А именно список инициализации . Значения между фигурными скобками сопоставляются с вашим конструктором, и, поскольку у вас есть конструктор с аргументами шаблона variadi c, ваш код компилируется. Другая причина в том, что вы предоставляете только литералы, а литералы имеют очень хорошо определенные типы: int
, const char[N]
, float
, .... Компилятор уже знает эти типы без необходимости указывать тип.
Теперь добавьте еще один уровень фигурных скобок, например:
{ 1, 2, { 3, 4, 5 }, "test" }
Согласно правилам инициализации списка (который используется из-за вашей внешней пары фигурных скобок), компилятор теперь ищет конструктор, который соответствует этой подписи:
Ctor(int, int, <unknown>, const char*)
Как вы можете видеть, компилятор не знает, какой тип {3, 4, 5}
, поэтому он пытается найти конструктор, который хотя бы соответствует всем другие аргументы. Если он находит его, он пытается передать часть <unknown>
тому типу, который соответствует соответствующей перегрузке в этой позиции в списке параметров. Затем это вызовет другую инициализацию списка для этого указанного c параметра, и весь процесс повторяется.
Давайте возьмем наш примерный список сверху
{ 1, 2, { 3, 4, 5 }, "test" }
и набор конструкторов
Ctor(int, float*, int, const char*); // A
Ctor(int, int, int, const char*); // B
Ctor(int, int, std::array<int, 3>, const char*); // C
Теперь компилятор пытается сопоставить все известные типы с мнимой сигнатурой, которую мы определили выше (да const char*
не является действительным типом литерала, но здесь мы его игнорируем):
Ctor(int, int, <unknown>, const char*)
Опция A немедленно исключается, поскольку типы 2-го аргумента не совпадают, поэтому у нас остаются B и C. Для B и C обе подписи соответствуют известным типам, поэтому нам осталось выяснить, что может быть <unknown>
. B предлагает нам int
и C std::array<int, 3>
. Давайте посмотрим B: можем ли мы сделать int x{3, 4, 5}
(это упрощение и только для иллюстрации)? Оказывается, C ++ не позволяет нам это делать, поэтому мы отбрасываем B как опцию. Далее C: мы можем сделать std::array<int, 3> x{3, 4, 5}
. Это выглядит правильно, C ++ позволяет нам это делать. Мы нашли возможное совпадение для <unknown>
, и компилятор выбирает C в качестве конструктора, который будет вызван. Отлично!
Теперь давайте объявим конструктор шаблона variadi c
template <typename ...Args>
Ctor(Args&&... args);
и сопоставим его с нашим списком. Поскольку у нас есть параметры шаблона, мы в основном говорим компилятору: берите тот тип, который нам дает аргумент. Итак, мы делаем это. Первые два и последние аргументы просты, они просто литералы, поэтому у нас есть
template <typename Arg3>
Ctor(int, int, Arg3&& arg3, const char*);
Теперь мы попробуем наш вложенный список. Аргументом является <unknown>
, потому что компилятор не может понять, что такое {3, 4, 5}
(мы оставляем std::initializer_list
здесь как особый случай). Параметр шаблона говорит: возьмите любой тип этого аргумента, а тип аргумента - <unknown>
. Вы замечаете проблему?