std::vector
использует для этих целей конструктор с параметром типа std::initializer_list<тип_элемента>
.
Раньше хорошим тоном считалось к нему в пару добавлять конструктор от двух итераторов. Но в прекрасном новом мире теперь в моде ranges, поэтому второй конструктор можно сделать примерно такой: (аргумент - любой тип с перегруженными .begin()
, .end()
, и т.п.)
template <std::ranges::input_range T>
requires( std::same_as<std::ranges::range_value_t<T>, тип_элемента>
&& !std::same_as<std::remove_cvref_t<T>, MyClass>
)
MyClass(T &&list) {}
(реализация у обоих конструкторов будет одинаковая, ее можно вынести в шаблонную функцию)
Есть еще вариативные шаблоны: template <typename ...P> MyClass(P &&... params)
- такой конструктор может принять несколько объектов разных типов. Но использовать его для списка однотипных объектов выглядит расточительно (под разное количество элементов генерируются отдельные экземпляры).
Если конструктор/функцию не хочется шаблонировать (чтобы принимать произвольный range), то лучшим вариантом будет принимать std::span<const тип_элемента>
- в него можно передать и список (в двойных скобках - {{1, 2, 3}}
, так что перегрузка с initializer_list
становится не очень нужна), и массив, и любой контейнер, элементы которого лежат непрерывно в памяти.
Если мы пишем функцию, а не конструктор, то перегрузки с range и с initializer_list
можно объединить в одну, написав template </*...*/ T = std::initializer_list<тип_элемента>>
. Почему-то с конструкторами фокус не проходит.
Если вы потом собираетесь полученный список класть в какой-то контейнер (например std::vector
), то лучше этот же тип сделать параметром конструктора (по значению, потом применить std::move
), и больше никакие конструкторы не писать.