Как применить if, если во время компиляции в С++
Я пытаюсь написать общую реализацию static_for
, которая может принимать ограничения, функцию приращения и функцию сравнения для запуска цикла. Я использую эту конструкцию с простыми циклами, которые увеличиваются на 1. В этом случае легко остановить разворот цикла, просто специализировавшись на IDX & END
, равном.
Однако, когда инкремент может быть с любым целым числом, не гарантируется, что IDX & END
всегда будет равным. Условие if
оценивается только во время выполнения. В нижеприведенном фрагменте кода я пытался специализироваться на std::false_type
, который останавливает рекурсию. Интеграл_констант строится путем оценки функциональности std::less
(который может быть заменен пользователем для любой другой оценки). К сожалению, этот функционал comparator
также оценивается только во время выполнения и, следовательно, компилятор не работает. Может ли кто-нибудь посоветовать, как это сделать?
ПРИМЕЧАНИЕ: использование С++ 11.
template <int idx, int end, typename eval, int count, typename comparator>
struct static_for_loop {
template <typename Lambda, typename... Args>
void operator()(const Lambda& function, Args... args) const {
if (comparator()(idx, end)) {
std::integral_constant<int, idx> i;
function(i, args...);
constexpr bool lesser = comparator()(idx + count, end);
static_for_loop<idx + count, end, std::integral_constant<bool, lesser>, count,
comparator>()(function, args...);
}
}
};
template <int idx, int end, int count, typename comparator>
struct static_for_loop<idx, end, std::false_type, count, comparator> {
template <typename Lambda, typename... Args>
void operator()(const Lambda& function, Args... args) const {}
};
template <int idx, int end, int count = 1, typename comparator = std::less<int>>
struct static_for {
template <typename Lambda, typename... Args>
void operator()(const Lambda& function, Args... args) const {
static_for_loop<idx, end, std::true_type, count, comparator>()(function, args...);
}
};
Ответы
Ответ 1
Мне проще просто обернуть все в объект:
template <int S, int E, int step>
struct iter {
auto next() { return iter<std::min(E, S+step), E, step>{}; }
};
И тогда у вас просто есть перегрузка для случая, когда она была выполнена, и в том случае, когда это не так:
template <int S, int E, int step, class F, class... Args>
void for_loop(iter<S, E, step> i, F func, Args... args) {
func(S, args...);
for_loop(i.next(), func, args...);
}
template <int E, int step, class F, class... Args>
void for_loop(iter<E, E, step>, F, Args... ) {
}
Например:
// prints 0 4 8
for_loop(iter<0, 10, 4>{}, [](int i){std::cout << i << ' ';});
В качестве альтернативы можно использовать enable_if
, чтобы отличать случаи, чтобы избежать необходимости в min
:
template <int S, int E, int step, class F, class... Args>
std::enable_if_t<(S<E)> for_loop(iter<S, E, step>, F func, Args... args)
{
func(S, args...);
for_loop(iter<S+step, E, step>{}, func, args...);
}
template <int S, int E, int step, class F, class... Args>
std::enable_if_t<!(S<E)> for_loop(iter<S, E, step>, F , Args... )
{
}
YMMV, на котором вы предпочитаете.
Ответ 2
Вы можете использовать sfinae для решения проблемы:
template <int idx, int end, typename eval, int count, typename Comparator>
struct static_for_loop {
template <typename Lambda, typename... Args>
auto operator()(Lambda&& function, Args&&... args) const
-> std::enable_if_t<Comparator{}(idx, end)> {
std::integral_constant<int, idx> i;
std::forward<Lambda>(function)(i, std::forward<Args>(args)...);
constexpr bool lesser = comparator{}(idx + count, end);
static_for_loop<
idx + count,
END,
std::integral_constant<bool, lesser>,
count,
Comparator
>()(std::forward<Lambda>(function), std::forward<Args>(args)...);
}
// do nothing when false
template <typename Lambda, typename... Args>
auto operator()(Lambda&& function, Args&&... args) const
-> std::enable_if_t<!Comparator{}(idx, end)> {
}
};
std::enable_if
выберет правильную функцию с помощью sfinae. Он будет действовать как время компиляции, если.
Я также использовал совершенную переадресацию, так как ваш код не работал во всех случаях, например, передавая некомпактный или изменяемый лямбда. Теперь это будет.
Если у вас нет С++ 14, вы можете написать typename std::enable_if<...>::type
вместо этого.
Попытайтесь использовать меньше всего имени в верхнем регистре, это повреждает лирику.
Ответ 3
Не проблема, что вы не указали comparator
? Просто укажите ваш API таким образом, чтобы comparator<IDX>::type
был std::true_type
, если цикл должен продолжаться для IDX
и останавливаться, когда он false_type
. Тогда ваш простой пример цикла использует template<int IDX> using Comp = std::integral_constant<bool, (IDX < 5
) > `.