#c #templates #recursion #c 17 #variadic-templates
#c #шаблоны #рекурсия #c 17 #переменные-шаблоны
Вопрос:
Я хочу создать функцию, которая принимает типы и снова вызывать себя с помощью цикла или рекурсии.
То, что я уже пробовал, было приведено ниже, но оно вызывало только базовую функцию (Вызывало базовую функцию с AModule
классом и не вызывало функцию с BModule.
class AModule {
};
class BModule {
};
auto main() -> int {
init<AModule, BModule>()
return 0;
}
template<typename Module>
void init() {
// BASE FUNCTION
// Do something
}
template<typename... Modules>
void init() {
(init<Modules>(), ...)
}
Комментарии:
1. Не могли бы вы пояснить, в чем проблема? Предоставляемый вами код вообще не должен компилироваться (
init
ранее не объявлялсяmain
) — и даже если вы исправите это, добавив недостающие точки с запятой, я бы не ожидал описываемого вами поведения (вызывает,init<A>
но неinit<B>
вызывает), я бы ожидал неоднозначного вызоваinit<B>
.
Ответ №1:
В коде вашего примера вы используете сворачивание шаблонов, новую функцию C 17, которая позволяет вам избежать рекурсии.
Но, чтобы избежать столкновения имен, я предлагаю называть базовую версию по-другому; скажем do_something()
template<typename Module>
void do_something() {
// BASE FUNCTION
// Do something
}
template<typename... Modules>
void init() {
(do_something<Modules>(), ...);
}
Если вы действительно хотите использовать рекурсивный способ, вы можете сделать что-то следующим образом
template <int = 0>
void init ()
{
// stop recursion; do nothing
}
template <typename Head, typename ... Tail>
void init ()
{
// do something with Head
// recursively call init()
init<Tail...>();
}
Хитрость в том, что вызов init<Tail...>();
, пока Tail...
не станет пустым, называется Head
/ Tail...
рекурсивной версией.
Когда Tail...
значение пусто, init<Tail...>()
это init<>()
значит, что Head
/ Tail...
version больше не соответствует ( Head
), но соответствует int = 0
версии; поэтому init<>()
становится init<0>()
, и регистр «ничего не делать» останавливает рекурсию.
Ответ №2:
В вашем коде (после исправления синтаксических ошибок) оба init()
вызова были допустимы для 1 параметра шаблона, поэтому вызов был неоднозначным. Если сделать так, чтобы вызов с несколькими инициализациями требовал как минимум 2 параметра, эта двусмысленность устраняется.
// bogus type that just prints an error message including the type it is parameterized with
template<typename T>
struct printer;
template<typename Module>
void init() {
printer<Module>{}; // this is an error just to show you that it's getting called with both types
}
// only call this if it's called with 2 or more types, otherwise just use the other init()
template<typename T1, typename T2, typename... Modules>
void init() {
init<T1>();
init<T2>();
(init<Modules>(), ...);
}
class AModule {
};
class BModule {
};
auto main() -> int {
init<AModule, BModule>();
return 0;
}