Подтвердить что ты не робот

С++ 11 для циклов без переменной цикла

В С++ мне нужно итератировать определенное количество раз, но мне не нужна переменная итерации. Например:

for( int x=0; x<10; ++x ) {
    /* code goes here, i do not reference "x" in this code */
}

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

Я надеялся, что С++ 11, основанный на диапазонах для циклов, поможет:

for( auto x : boost::irange(0,10) ) {
    /* code goes here, i do not reference "x" in this code */
}

но приведенное выше дает "непринятую локальную переменную", поскольку я никогда не ссылаюсь на x.

Мне интересно, есть ли более элегантный способ написать выше для циклов, чтобы код не генерировал предупреждение "unreferenced local variable".

4b9b3361

Ответ 1

Там может быть способ сделать это, но я очень сомневаюсь, что это будет более элегантно. То, что у вас в этом первом цикле, уже является правильным способом для этого, ограничивая область/время жизни переменной цикла.

Я бы просто проигнорировал предупреждение неиспользуемой переменной (это всего лишь указание от компилятора, что что-то может быть не так, в конце концов) или использовать средства компилятора (если доступно), чтобы просто отключить предупреждение в этой точке.

Это может быть возможно с каким-то #pragma в зависимости от вашей среды, или некоторые реализации позволяют вам делать такие вещи, как:

for (int x = 0; x < 10; ++x) {
    (void)x;

    // Other code goes here, that does not reference "x".
}

Я видел, что трюк void используется для неиспользуемых параметров в телах функций.

Ответ 2

Изменить теперь со 100% меньшими объявленными переменными цикла.

template <typename F>
void repeat(unsigned n, F f) {
    while (n--) f();
}

Используйте его как:

repeat(10, f);

или

repeat(10, [] { f(); });

или

int g(int);
repeat(10, std::bind(g, 42));

Смотрите в прямом эфире http://ideone.com/4k83TJ

Ответ 3

Предполагая, что 10 является константой времени компиляции...

#include <cstddef>
#include <utility>
template<std::size_t N>
struct do_N_times_type {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
    closure();
    do_N_times_type<N-1>()(std::forward<Lambda>(closure));
  }
};
template<>
struct do_N_times_type<1> {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
    std::forward<Lambda>(closure)();
  }
};
template<>
struct do_N_times_type<0> {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
  }
};

template<std::size_t N, typename Lambda>
void do_N_times( Lambda&& closure ) {
  do_N_times_type<N>()( std::forward<Lambda>(closure) );
};
#include <iostream>
void f() {
  std::cout << "did it!\n";
}
int main() {
  do_N_times<10>([&]{
    f();
  });
}

или просто

int main() {
  do_N_times<10>(f);
}

Другие смешные методы:

Напишите итератор диапазона (я вызываю my index), который создает диапазон типов итераторов на интеграле (по умолчанию я std::size_t). Затем введите:

for( auto _:index_range(10) )

который использует переменную (_), но выглядит чрезвычайно запутанной.

Другим сумасшедшим подходом было бы создание генератора, подобного питону. Написание оболочки-генератора, которая принимает итерируемый диапазон и создает функцию, которая возвращает std::optional в value_type диапазона, не является сложной.

Тогда мы можем:

auto _ = make_generator( index_range(10) );
while(_()) {
}

который также создает временную переменную и еще более тупые.

Мы могли бы написать циклическую функцию, которая работает с генераторами:

template<typename Generator, typename Lambda>
void While( Generator&& g, Lambda&& l ) {
  while(true) {
    auto opt = g();
    if (!opt) return;
    l(*opt);
  }
}

который мы тогда называем:

While( make_generator( index_range(10) ), [&](auto&&){
  f();
});

но это создает как временные переменные в функции, так и более смешно, чем последние, и полагается на функции С++ 1y, которые еще не были финализированы.

Те, где мои попытки создать способ без переменной, чтобы повторить что-то 10 раз.

Но на самом деле, я бы просто сделал цикл.

Вы почти наверняка заблокируете предупреждение, набрав x=x;

Или напишите функцию

template<typename Unused>
void unused( Unused&& ) {}

и вызов unused(x); - используется переменная x, и ее имя заносится внутри, поэтому компилятор может не предупредить вас об этом внутри.

Итак, попробуйте следующее:

template<typename Unused>
void unused( Unused&& ) {}
for(int x{};x<10;++x) {
  unused(x);
  f();
}

который должен подавлять предупреждение, и на самом деле легко понять.

Ответ 4

На самом деле есть способ сделать эту работу. Все, что вам нужно сделать, это вернуть std::array с длиной, указанной константой, которую вы предоставляете:

template <int N>
using range = std::array<int, N>;

int main()
{
    for (auto x : range<5>())
    {
        std::cout << "Awesome\n";
    }
}

Вывод:

Высокий
Высокий
Высокий
Высокий
Высокий

Ниже приведена демонстрация.

Примечание. Предполагается, что спецификатор диапазона является константой времени компиляции, поэтому, если вам нужно использовать переменную, убедитесь, что она действительно отмечена constexpr.

Ответ 5

Нет никакого способа сделать диапазон, основанный на работе, для простое повторение нескольких чисел.

Контуры, основанные на диапазонах С++ 11, требуют выражения в диапазоне, которое может быть:

  • массив или
  • класс, имеющий
    • Функции-члены begin() и end() или
    • доступные свободные функции begin() и end() (через ADL)

В дополнение к этому: диапазон, основанный на некоторых издержках:

for ( for_range_declaration : expression ) statement

расширяется до

range_init = (expression)
{
  auto && __range = range_init;
  for ( auto __begin = begin_expr,
  __end = end_expr;
  __begin != __end;
  ++__begin ) {
    for_range_declaration = *__begin;
    statement;
  }
}

Где begin_expr и end_expr получены через проверку массива или пары begin()/end().

Я не думаю, что это становится "чище", чем простой for-loop. Особенно в отношении производительности. Нет вызовов, только простой цикл.

Единственный способ, которым я могу понять, чтобы сделать его более изящным (где элегантно, по-моему, зависит от моего мнения), можно использовать здесь размер или беззнаковый тип:

for(size_t x(0U); x<10U; ++x) f();

Ответ 6

По-моему, вы злоупотребляете циклом на основе диапазона. Цикл на основе диапазона должен использоваться, когда логика: "для каждого элемента в коллекции что-то делать". Вся идея состоит в том, чтобы избавиться от индексной переменной, поскольку она не важна. Если у вас есть коллекция, вы должны использовать ее с необходимыми API для включения итерации на основе диапазона. Если у вас нет коллекции, вам не нужно использовать цикл на основе диапазона (на самом деле это то, что компилятор подразумевает не столь информативным образом). В этой ситуации нормальный для цикла /while является естественным выбором.

Ответ 7

Как лучше ответить в fooobar.com/questions/122094/...: как определить макрос UNUSED, который будет использоваться на всей вашей кодовой базе, что подавляет это предупреждение. Портативным способом.

Ответ 8

Вы можете использовать STL вместе с выражением лямбда.

#include <algorithm>
#include <iostream>

int main() {
    int a[] = {1,2,3,4,5,6};

    std::for_each(std::begin(a),
            std::end(a),
            [](int){std::cout << "Don't care" << std::endl;});
}

Этот подход также работает для любых контейнеров, таких как векторы или списки. Пусть vector<int> a, тогда вы назовёте a.begin() и a.end(). Обратите внимание, что вы также можете использовать указатель на функцию вместо выражения лямбда.

Приведенное выше сохраняет ваше понятие использования foreach, не жалуясь на неиспользованный параметр.

Ответ 9

это работает в GCC и clang и любом компиляторе, который поддерживает атрибуты gnu:

for( [[gnu::unused]] auto x : boost::irange(0,10) ) {

и должен компилироваться в любом компиляторе С++ 11, но не может подавлять предупреждение, если компилятор не распознает атрибуты gnu.

Ответ 10

Чтобы присоединиться к конкурсу:

#include <iostream>
#include <boost/range/irange.hpp>
using namespace std;
using namespace boost;

namespace {
    template<typename T> inline void 
    unused(const T&) {}
}

int main() {
    for (auto&& i : irange(0,10)) {
        unused(i);
        cout << "Something" << endl;
    }
    return 0;
}