Есть ли способ определить переменные двух разных типов в инициализаторе цикла?

Вы можете определить 2 переменные одного и того же типа в цикле for:

int main() {
  for (int i = 0, j = 0; i < 10; i += 1, j = 2*i) {
    cout << j << endl;
  }
}

Но нелегально определять переменные разных типов:

int main() {
  for (int i = 0, float j = 0.0; i < 10; i += 1, j = 2*i) {
    cout << j << endl;
  }
}

Есть ли способ сделать это? (Мне не нужно использовать i внутри цикла, просто j.)

Если у вас есть полностью взломанное и непонятное решение, это нормально для меня.

В этом надуманном примере я знаю, что вы можете просто использовать double для обеих переменных. Я ищу общий ответ.

Пожалуйста, не предлагайте перемещать какие-либо переменные за пределами тела, возможно, не используемые для меня, поскольку один из них - это итератор, который должен исчезнуть сразу после цикла, а оператор for должен быть заключен в мой макрос foreach

#define foreach(var, iter, instr) {                  \
    typeof(iter) var##IT = iter;                     \
    typeof(iter)::Element var = *var##IT;            \
    for (; var##_iterIT.is_still_ok(); ++var##IT, var = *var#IT) {  \
      instr;                                         \
    }                                                \
  }

Его можно использовать таким образом:

foreach(ii, collection, {
  cout << ii;
}). 

Но мне нужно что-то, что будет использовано так:

foreach(ii, collection)
  cout << ii;

Пожалуйста, не вводите служебные данные во время выполнения (но это может быть медленным для компиляции).

Ответ 1

Вот версия с использованием препроцессора boost (это просто для удовольствия. Для ответа на реальный мир см. @kitchen один выше):

FOR((int i = 0)(int j = 0.0), i < 10, (i += 1, j = 2 * i)) { 

}

В первой части указывается последовательность объявлений: (a)(b).... Переменные, объявленные позже, могут ссылаться на объявленные перед ними переменные. Вторая и третья части, как обычно. Когда запятые появляются во второй и третьей частях, скобки могут использоваться для предотвращения их разделения аргументов макроса.

Известны два трюка, которые я использовал для объявления переменных, которые позже видны в составной инструкции, добавленной за пределами макроса. Первый использует условия, например if:

if(int k = 0) ; else COMPOUND_STATEMENT

Затем отображается k. Естественно, всегда нужно оценивать значение false. Поэтому он не может быть использован нами. Другим контекстом является следующий:

for(int k = 0; ...; ...) COMPOUND_STATEMENT

Что я буду использовать здесь. Нам нужно будет посмотреть только одну итерацию COMPOUND_STATEMENT. Фактический цикл for, который выполняет проверку приращений и условий, должен заканчиваться, поэтому к нему добавляется прилагаемый составной оператор.

#include <boost/preprocessor.hpp>
#include <iostream>

#define EMIT_DEC_(R,D,DEC) \
    for(DEC; !_k; ) 

#define FOR(DECS, COND, INC) \
    if(bool _k = false) ; else \
      BOOST_PP_SEQ_FOR_EACH(EMIT_DEC_, DECS, DECS) \
        for(_k = true; COND; INC)

int main() {
    FOR((int i = 0)(float j = 0.0f), i < 10, (i += 1, j = 2 * i)) {
        std::cout << j << std::endl;
    }
}

Он создает группу операторов for, каждая из которых вложена в другую. Он распространяется на:

if(bool _k = false) ; else
  for(int i = 0; !_k; )
    for(float j = 0.0f; !_k; )
      for(_k = true; i < 10; (i += 1, j = 2 * i)) {
        std::cout << j << std::endl;
      }

Ответ 2

Пожалуйста, не предлагайте перемещать какой-либо из переменные за пределами тела, вероятно, не пригодны для меня, как итератор должен исчезнуть сразу после цикл.

Вы можете сделать это:

#include <iostream>

int main( int, char *[] ) {
    {
        float j = 0.0;

        for ( int i = 0; i < 10; i += 1, j = 2*i ) {
            std::cout << j << std::endl;
        }
    }

    float j = 2.0; // works

    std::cout << j << std::endl;

    return 0;
}

Ответ 3

Ну, это уродливо. Но вы можете использовать пару.

int main() {
  for (std::pair<int,float> p(0,0.0f); 
       p.first < 10; 
       p.first += 1, p.second = 2*p.first) {
    cout << p.second << endl;
  }
}

Ответ 4

{
  int i = 0;
  float j = 0.0;
  for ( ; i < 10; i += 1, j = 2*i) {
    cout << j << endl;
  }
}

Переменные "исчезают" после блока.

Ответ 5

Это приведет к тому, что итератор (или в этом случае float) исчезнет, ​​когда он больше не понадобится:

int main() {
  // some code...

  {
    float j = 0.0;
    for (int i = 0; i < 10; i += 1, j = 2*i) {
      cout << j << endl;
    }
  }

  // more code...
}

Ответ 6

Если у вас проблемы с макросами, существует стандартный трюк do..while, который отлично работает:

#define MYFOR(init, test, post, body) \
    do \
    { \
        init \
        for( ; test; post) \
            body \
    } while(0)

Используйте его следующим образом:

MYFOR( int i = 0; float j = 0.0f; , i < 10 , (i += 1, j = 2.0f * i),
    {
         cout << j << endl;
    } );

Это уродливо, но он делает то, что вы хотите: область i и j ограничена циклом do..while из макроса, и в конце требуется точка с запятой, поэтому вы не получите укушен, помещая его в предикат инструкции if/else.

Ответ 7

Этот также уродливый, но также предоставляет общий способ для объявления нескольких переменных с определенным именем и типами в цикле

int main() {
  for (struct { int i; float j; } x = { };
       x.i < 10; x.i += 1, x.j = 2 * x.i) {
    cout << x.j << endl;
  }
}

Ответ 8

РЕДАКТИРОВАТЬ. Вопрос изменился еще раз. Теперь вопрос явно хочет реализовать цикл foreach. Самый простой ответ:

#include <boost/foreach.hpp>
void( std::vector<int>& v ) {
   BOOST_FOREACH( int & x, v ) {
      x = x*2;
   }
}

Ввод переменной в кодовый блок

Это не предназначено как ответ, а для того, чтобы показать более общий метод для ввода переменной в кодовый блок. Кажется, что макрос OP, который пытается определить, может использовать, даже если он наносит некоторые служебные

Есть несколько мест, где вы можете определить переменную с разными областями. Вы можете определить переменную внутри любого блока кода, и ее продолжительность будет до конца этого конкретного блока. Вы можете определить переменную в скобках цикла for, а область будет циклом цикла. Вы также можете определить переменную в блоке if, и ее область действия будет иметь значение if (включая предложение else).

Вы можете объединить эти параметры выше, чтобы создавать внешние и вводить переменные в блок кода, не создавая переменную, срок службы которой превышает срок действия блока. Практическим примером будет определение цикла foreach (упрощенного для работы только с контейнерами STL). Синтаксисом вызова будет:

void f( std::vector<int>& container ) 
{
   INTVECTOR_FOREACH( int & x, container )
   {
      x = x*2;
   }
}

С семантикой, подобной foreach в других языках: x получает ссылку на каждый элемент в контейнере, так что функция фактически удваивает каждое значение внутри целочисленного вектора.

Теперь код упрощенного макроса:

#define INTVECTOR_FOREACH( variable, container ) \
   for ( std::vector<int>::iterator it = container.begin(); it!=container.end(); ++it ) \
      if ( bool condition=false ) {} else \
         for ( variable = *it; !condition; condition=true )

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

Внешний для итерации над контейнером, на каждой итерации мы выполняем другую только для определения переменной итерации (int и x в примере кода). Нам нужно условие для управления числом итераций (1) внутреннего цикла, и этому условию вводится if. Мы решили сделать сбой if, чтобы мы могли гарантировать, что пользователь не получит неожиданные результаты, если она напишет другое после цикла... макросы сложны.

Ответ 9

Пожалуйста, не предлагайте перемещать какие-либо переменные за пределами тела, возможно, не используемые для меня, поскольку итератор должен исчезнуть сразу после цикла.

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

int main() 
{
  {
    float j = 0.0;
    for (int i = 0; i < 10; i += 1, j = 2*i) 
    {
      cout << j << endl;
    }
  }
  // more code...
}

Таким образом, j выйдет за пределы области сразу после цикла.

Ответ 10

С требованиями, которые вы даете простейшим кодом, я могу думать:

for ( int i = 0; i < 10; ++i )
{
   float f = i * 2;
   std::cout << f << std::endl;
}

Вы используете только f в два раза больше значения i. Время жизни ограничено циклом и (по крайней мере, в упрощенном вопросе, который вы предоставляете) поплавки дешевы для создания (точно так же дешево, как и для назначения).

Если построение реального float (я предполагаю, что, поскольку я не является int, f не может быть float), намного дороже, чем переназначение значения, тогда другие решения инкапсулирования внутри дополнительной пары фигурные скобки для ограничения области будут лучшим вариантом.

Ответ 11

int main() {
  for (int i = 0, float j = 0.0; i < 10; i += 1, j = 2*i) {
    cout << j << endl;
  }
}

Может быть, я плотный, но зачем вам даже объявлять поплавок? Вы просто "выбросите его", когда вы все равно покинете петлю. Правильно?

for(int i=0; i<10; ++i)
    cout << (float)2*i << endl;

Зачем вам нужен j?

Ответ 12

Вы говорите, что i - это ваш собственный тип, и вам просто нужно сгенерировать j из i, правильно? Легко. Добавьте функцию-член в класс i для генерации значения j и всегда используйте это. Возможно, вы даже можете сделать макрос, чтобы "скрыть" вызов этой функции-члена, если хотите.: -)

Ответ 13

Почему бы вам просто не объявить и не инициализировать переменные за пределами цикла for? Вы все еще можете тестировать и увеличивать его, как сейчас.