Есть ли способ определить переменные двух разных типов в инициализаторе цикла?
Вы можете определить 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? Вы все еще можете тестировать и увеличивать его, как сейчас.