Есть ли способ преобразовать этот макрос препроцессора "для i" c ++ в современный C ++ (11+)? - PullRequest
4 голосов
/ 26 марта 2019

Я ищу способ заменить этот макрос препроцессора C ++ на что-то более современное.

#define fori(FORI_TYPE, FORI_FROM, FORI_TO) \
            for(FORI_TYPE i{FORI_FROM}; \
            ((FORI_FROM) < (FORI_TO)) ? (i < (FORI_TO)) : (i > (FORI_TO)); \
            ((FORI_FROM) < (FORI_TO)) ? ++i : --i )

В идеале я смог бы избавиться от всего?операторы (может ли здесь быть полезен constexpr?) и что «fori» не несет никаких накладных расходов, как это происходит сейчас с версией процессора (оценки операторов?).Также введите safety.

Пример использования:

fori(size_t, 0, n)
{
    cout << i << endl;
}

Ответы [ 3 ]

7 голосов
/ 26 марта 2019

Что бы это ни стоило, вам всегда нужно знать, в каком направлении идти, поэтому вы не сможете избавиться от этих накладных расходов.Тем не менее, переключаясь с макросов, вы можете, по крайней мере, упростить оптимизацию (частично, сделав входные данные const, чтобы обеспечить складывание дубликатов / похожих условий на них, и частично, предварительно рассчитав «шаговое» расстояние, чтобы исключить некоторыеиз этих условий полностью).

Что касается макросов, то это не так уж плохо (хотя, вероятно, можно использовать () или два ...).

По-настоящему "современный"что нужно сделать, это использовать подсчитывающий итератор или что-то, связанное с irange.

Например, наивная адаптация кода Нила для обеспечения автоматического направления шагаобнаружение:

#include <iostream>

template <class T>
class range
{
private:
    class iter
    {
    private:
        T at, step;
    public:
        iter(T at, T step) : at(at), step(step) {}
        bool operator!=(iter const& other) const { return at != other.at; }
        T const& operator*() const { return at; }
        iter& operator++() { at += step; return *this; }
    };

    T begin_val;
    T end_val;
    T step_val;

public:

    range(const T begin_val, const T end_val)
        : begin_val(begin_val)
        , end_val(end_val)
        , step_val(begin_val > end_val ? -1 : 1)
    {}

    iter begin() { return iter(begin_val, step_val); }
    iter end() { return iter(end_val, step_val); }
};

int main()
{
   for (auto i : range<unsigned>(42, 10))
      std::cout << i << ' ';
   std::cout << '\n';
}

// Output: 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 

( live demo )

Или, если быть откровенным, вы можете просто написать следующее и покончить с этим:

#include <iostream>

int main()
{
   for (unsigned int i = 42; i > 10; --i)
      std::cout << i << ' ';
   std::cout << '\n';
}

// Output: 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 

( live демо )

Все это поймут;никаких трюков не требуется.

В любом случае, и, несмотря на мои примеры выше, я бы действительно советовал , а не , используя неподписанные типы для этого .

0 голосов
/ 26 марта 2019

Чтобы сгенерировать последовательность типа, мы бы хотели использовать генератор.Диапазон для будет использовать его вывод:

for(auto i: range<size_t>(0, n)) {
  std::cout << n << " ";
}

И:

template <typename T> class range {
  T a, b;
public:
  typename wrap<T> begin() const { return a; }
  typename wrap<T> end() const { return b; }
};

Оболочка будет наполнять значение некоторыми свойствами (типами и методами), которые должен иметь итератор, но скореепросто для числовых типов.

0 голосов
/ 26 марта 2019

Мы можем избавиться от макроса с помощью шаблона, хотя это означает необходимость добавления функции обратного вызова.

//f is of the form void func(int i)
template <typename T, typename F>
void fori(const T from, const T to, F f)
{
    for(
        T i  = from;
        (from < to) ? (i < to) : (i > to);
        (form < to) ? ++i : --i 
    ) {
         f(i);
 }

использование:

fori<int>(1, 20, [](int i) {
    std::cout << i << std::endl;
});

Это также, вероятно, можно улучшить с помощью концепций C ++ 20.

...