Как применить 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) > `.