C++, неизвестное число параметров в конструкторе
Понадобилось тут создавать объекты, вызывая конструкторы с неизвестным числом аргументов. И главная проблема: а сколько параметров передавать? И если для функций и методов можно взять указатель и разобрать шаблоном на кирпичики, с конструктором такое не прокатит: получить указатель на конструктор нельзя.
template <typename Return, typename Obj, typename... Args>
constexpr int number_of_arguments(Return (Obj::*fp)(Args...) const) {
return sizeof...(Args);
}
Облегчает задачу, что сами типы аргументов конструктора известны. Они все должны выводиться из некоторого общего типа, условно, “Any
”. Тогда можно пробовать вызывать конструктор с N+1 аргументами, и считать количество, с которым объекту-таки удалось создаться.
Сначала получим число параметров, рекурсивно проверяя конструктор, с каждым шагом увеличивая число параметров на один.
template <int count, typename Any, typename T, typename... Args>
constexpr auto arguments_count() -> std::enable_if_t<std::is_constructible<T, Args...>::value, int> {
return count;
}
template <int count, typename Any, typename T, typename... Args>
constexpr auto arguments_count() -> std::enable_if_t<!std::is_constructible<T, Args...>::value, int> {
static_assert(count < 20, "Max constructor arguments");
return arguments_count<count + 1, Any, T, Args..., Any>();
}
С полученным числом аргументов уже можно вызывать конструктор. Но нужно как-то собрать список из Any
длиной arguments_count
. И если схлопнуть последовательность можно удобно через index_sequence
, то чтобы расковырять её обратно без хелпера не обойтись. И трикшот с оператором запятая, чтобы вместо каждого числа подсунуть аргументом Any
:
template <typename T, typename Pack>
struct Constructor;
template <typename T, std::size_t... arguments>
struct Constructor<T, std::integer_sequence<std::size_t, arguments...>> {
template <typename Any>
static constexpr T construct(const Any &any) {
return T(((void)arguments, any)...);
}
};
И, собственно, вызов:
template <typename T, typename Any>
constexpr T construct(const Any &any) {
return Constructor<T, std::make_index_sequence<arguments_count<0, Any, T>()>>::construct(any);
}
Пример использования в реальной жизни. godbolt
struct A {
A(int a, int b): v(a + b) {}
int v;
};
struct B {
int v = 1;
};
int main() {
construct<A>(1);
construct<B>(2);
}