Время для некоторых злоупотреблений.
Мы собираемся написать функцию, которая будет определять, какой тип соответствует данному индексу. Но мы не можем вернуть этот тип - тип возврата должен быть известен во время компиляции. Поэтому вместо этого мы перешлем это на функцию, которую мы предоставляем. Начну с конечным результатом:
std::unique_ptr<A> chooseB(int i, int j)
{
using choices = typelist<int, double>;
return pick_elem<std::unique_ptr<A>>(i, choices{},
[=](auto tag1){
return pick_elem<std::unique_ptr<A>>(j, choices{},
[=](auto tag2) {
using T1 = typename decltype(tag1)::type;
using T2 = typename decltype(tag2)::type;
return std::make_unique<B<T1, T2>>();
});
});
}
pick_elem
принимает три аргумента: индекс, список выбора, и функции для пересылки вместе с. Итак, мы называем это первым индексом и вызываем лямбда - который по очереди вызывает его со вторым индексом и вызывает другую лямбду, что, наконец, делает наш B
.
Вы должны явно указать тип возврата pick_elem<>
, потому что ему необходимо как-то остановить рекурсию, и этот конечный шаг должен будет знать, что нужно вернуть. Итак, мы начинаем с некоторыми удобными помощниками метапрограммирования:
template <class... > struct typelist {};
template <class T> struct tag { using type = T; };
А потом pick_elem
является:
template <class R, class F>
R pick_elem(int , typelist<>, F)
{
throw std::runtime_error("wtf");
}
template <class R, class T, class... Ts, class F>
R pick_elem(int i, typelist<T, Ts...>, F f)
{
if (i == 0) {
return f(tag<T>{});
}
else {
return pick_elem<R>(i-1, typelist<Ts...>{}, f);
}
}
Это может быть обобщена на сколь угодно много типов, делая chooseB
сам VARIADIC. В некотором смысле, это на самом деле чище, чем у меня раньше. chooseB()
теперь дополнительно принимает некоторые не выводимые аргументы, которые типы мы выяснили до сих пор:
template <class... Ts>
std::unique_ptr<A> chooseB()
{
return std::make_unique<B<Ts...>>();
}
template <class... Ts, class Idx, class... Rest>
std::unique_ptr<A> chooseB(Idx i, Rest... rest)
{
using choices = typelist<int, double>;
return pick_elem<std::unique_ptr<A>>(i, choices{},
[=](auto tag){
return chooseB<Ts..., typename decltype(tag)::type>(rest...);
});
}
Вы можете добавить некоторую безопасность вокруг этого, утверждая, что число аргументов является правильным.
Являются ли параметры 'selectB' известными во время компиляции? –
Нет. Например, мне может понадобиться построить B на основе анализируемой информации. (Я отредактировал вопрос, чтобы сделать это ясно). – dexterurbane
Похоже, вы ищете [зависимые типы] (https://en.wikipedia.org/wiki/Dependent_type), которые C++ не поддерживает. Хотя могут быть обходные пути, как это было предложено в ответе. – Elazar