Чи можливо оголосити дві змінні різних типів у циклі for?


240

Чи можливо оголосити дві змінні різних типів в тілі ініціалізації циклу a для C ++?

Наприклад:

for(int i=0,j=0 ...

визначає два цілих числа. Чи можу я визначити а intта а charв тілі ініціалізації? Як би це було зроблено?


3
Можна в g ++ - 4.4 ( -std=c++0x) у вигляді for(auto i=0, j=0.0; ..., але ця можливість була вилучена в g ++ - 4.5, щоб збігатися з текстами c ++ 0x.
rafak

Відповіді:


133

C ++ 17 : Так! Вам слід використовувати структуровану обов'язкову декларацію . Синтаксис підтримується в gcc і clang протягом багатьох років (з моменту gcc-7 та clang-4.0) ( живий приклад clang ). Це дозволяє нам розпакувати такий кортеж:

for (auto [i, f, s] = std::tuple{1, 1.0, std::string{"ab"}}; i < N; ++i, f += 1.5) {
    // ...
}

Наведене вище:

  • int i встановлений в 1
  • double f встановлений в 1.0
  • std::string s встановлений в "ab"

Переконайтеся, що #include <tuple>для цього виду декларації.

Ви можете вказати точні типи всередині tuple, ввівши їх усі, як у мене std::string, якщо ви хочете назвати тип. Наприклад:

auto [vec, i32] = std::tuple{std::vector<int>{3, 4, 5}, std::int32_t{12}}

Конкретним застосуванням цього є повторення карти, отримання ключа та значення,

std::unordered_map<K, V> m = { /*...*/ };
for (auto& [key, value] : m) {
   // ...
}

Дивіться живий приклад тут


C ++ 14 : Ви можете зробити те ж саме, що і C ++ 11 (нижче) з додаванням на основі типу std::get. Тож замість std::get<0>(t)наведеного нижче прикладу ви можете мати std::get<int>(t).


C ++ 11 : std::make_pairдозволяє це зробити, як і std::make_tupleдля більш ніж двох об’єктів.

for (auto p = std::make_pair(5, std::string("Hello World")); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}

std::make_pairповерне два аргументи в a std::pair. Доступ до елементів можна отримати за допомогою .firstта .second.

Для більш ніж двох об’єктів вам потрібно буде використовувати std::tuple

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    std::cout << std::get<1>(t) << std::endl; // cout Hello world
    std::get<2>(t).push_back(std::get<0>(t)); // add counter value to the vector
}

std::make_tupleце варіативний шаблон, який побудує набір будь-якої кількості аргументів (з деякими технічними обмеженнями, звичайно). Елементи можна отримати за допомогою індексу за допомогоюstd::get<INDEX>(tuple_object)

У телах for для циклу ви можете легко псевдонімувати об'єкти, хоча все-таки потрібно використовувати .firstабо std::getдля умови циклу та оновити вираз

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    auto& i = std::get<0>(t);
    auto& s = std::get<1>(t);
    auto& v = std::get<2>(t);
    std::cout << s << std::endl; // cout Hello world
    v.push_back(i); // add counter value to the vector
}

C ++ 98 та C ++ 03 Ви можете чітко назвати типи a std::pair. Не існує стандартного способу узагальнити це більш ніж двома видами:

for (std::pair<int, std::string> p(5, "Hello World"); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}

5
Якщо ви робите C ++ 17, можете навіть кинути make_та написати std::pair(1, 1.0).
Марк Глісс

Волохатий бізнес у стилі C ++ 14 - це все добре (напевно, підкреслив), але виглядає химерно :)
mlvljr

3
Якщо коротко: так, це можливо, але не буде красивим.
Якийсь програміст чувак

Так, не дуже, але це допінг! Абсолютно сподобався кортеж-іш. :) Але насправді це дуже неінтуїтивна синтаксична якість циклів на C ++ і дала мені головний біль більше півгодини, щоб нарешті зрозуміти, що треба було гуглом ...
aderchox

@aderchox, якщо ви можете роз'яснити своє непорозуміння, я можу оновити відповідь
Ryan Haining

276

Ні - але технічно існує обхід (не те, що я б фактично ним користувався, якщо не змушений):

for(struct { int a; char b; } s = { 0, 'a' } ; s.a < 5 ; ++s.a) 
{
    std::cout << s.a << " " << s.b << std::endl;
}

3
Це не компілюється в VS 2008, але є в Comeau в Інтернеті ;-)
JRL

7
@JRL: О, як і VS2005. Ще одна особливість недотримання в VC ++, я думаю.
Георг Фрітше

3
Я зробив еквівалент в Perl. Я ще не намагався прокрасти щось подібне через перегляд коду на C ++.
Джон

21
з c ++ 11 I ви можете зробити цей приклад коротшим, використовуючи значення за замовчуваннямstruct { int a=0; char b='a'; } s;
Ryan Haining

1
Ця відповідь відповідає вимогам відповіді, але з читабельності POV я віддаю перевагу @MK. відповідь. Рішення МК навіть вирішує масштабування, додаючи фігурні дужки.
Тревор Бойд Сміт

221

Не можливо, але ви можете:

float f;
int i;
for (i = 0,f = 0.0; i < 5; i++)
{
  //...
}

Або явно обмежте сферу застосування fта iвикористання додаткових дужок:

{
    float f; 
    int i;
    for (i = 0,f = 0.0; i < 5; i++)
    {
       //...
    }
}

Я знаю, що це дуже давнє запитання, але чи можете ви пояснити, чому деякі роблять це з додатковими дужками навколо нього, як у вашому другому прикладі?
форд

13
@fizzisist чітко обмежує область f і i лише частинами коду, де вони використовуються.
МК.

1
@MK. Спасибі, саме це я підозрював. Я відредагував вашу відповідь, щоб пояснити це.
форд

Лише одне питання: чому так? : O
rohan-patel

Тому що він працює як "int a = 0, b = 4", я припускаю. Це, мабуть, буде корисним лише для запобігання повторному використанню цих імен (що є справедливою причиною), але згенерований код, як правило, буде однаковим у сучасному компіляторі (у цьому випадку).
Асу

14

Ви не можете оголосити кілька типів при ініціалізації, але ви можете призначити кілька типів EG

{
   int i;
   char x;
   for(i = 0, x = 'p'; ...){
      ...
   }
}

Просто задекларуйте їх у власному обсязі.


3

Я думаю, що найкращий підхід - це відповідь Сяна .

але ...


# Вкладено для циклу

Цей підхід є брудним, але може вирішити у будь-якій версії.

тому я часто використовую його в макро-функціях.

for(int _int=0, /* make local variable */ \
    loopOnce=true; loopOnce==true; loopOnce=false)

    for(char _char=0; _char<3; _char++)
    {
        // do anything with
        // _int, _char
    }

Додатковий 1.

Також його можна використовувати declare local variablesі initialize global variables.

float globalFloat;

for(int localInt=0, /* decalre local variable */ \
    _=1;_;_=0)

    for(globalFloat=2.f; localInt<3; localInt++) /* initialize global variable */
    {
        // do.
    }

Додаткові 2.

Хороший приклад: з макро функцією.

(Якщо найкращий підхід не може бути використаний, оскільки це макрос для циклу)

#define for_two_decl(_decl_1, _decl_2, cond, incr) \
for(_decl_1, _=1;_;_=0)\
    for(_decl_2; (cond); (incr))


    for_two_decl(int i=0, char c=0, i<3, i++)
    {
        // your body with
        // i, c
    }

# Трюк-висловлювання

if (A* a=nullptr);
else
    for(...) // a is visible

Якщо ви хочете ініціалізіруетесь 0або nullptr, ви можете використовувати цей трюк.

але я не рекомендую цього через важке читання.

і, здається, помилка.


Мене ніколи не перестає дивувати, наскільки різні люди думають від інших. Я б ніколи не думав про такі дивацтва. Цікаві ідеї.
Доктор Персона Особа II

1

Див. " Чи існує спосіб визначення змінних двох типів для циклу? " Для іншого способу, що включає введення множин для циклів. Перевага іншого способу перед "структурним трюком" Георга полягає в тому, що він (1) дозволяє мати суміш статичних і нестатичних локальних змінних і (2) дозволяє мати змінні, що не можна скопіювати. Мінус у тому, що він набагато менш читабельний і може бути менш ефективним.


-2

Визначте макрос:

#define FOR( typeX,x,valueX,  typeY,y,valueY,  condition, increments) typeX x; typeY y; for(x=valueX,y=valueY;condition;increments)

FOR(int,i,0,  int,f,0.0,  i < 5, i++)
{
  //...
}

Пам’ятайте лише, що ваші змінні сфери застосування також не будуть знаходитись у циклі for.


Ви можете легко подолати це обмеження, загорнувши код у макрос в окрему область, використовуючи {та }.
Натан Осман

4
Ні, він не міг. Його макрос не завершує тіло циклу. Він міг би додати додаткову дужку розкриття, але для використання макросу потрібна додаткова дужка закриття.
Джон

3
Це цікава ідея, але я швидше використав би будь-яку з інших відповідей, перш ніж розглянути це.
gregn3

-2

Також ви можете використовувати як нижче в C ++.

int j=3;
int i=2;
for (; i<n && j<n ; j=j+2, i=i+2){
  // your code
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.