Apakah mungkin untuk mendeklarasikan dua variabel dari tipe berbeda dalam for for loop?

240

Apakah mungkin untuk mendeklarasikan dua variabel dari tipe yang berbeda di tubuh inisialisasi dari for for di C ++?

Sebagai contoh:

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

mendefinisikan dua bilangan bulat. Bisakah saya mendefinisikan intdan chardi dalam tubuh inisialisasi? Bagaimana ini akan dilakukan?

Nathan Osman
sumber
3
Dimungkinkan dalam g ++ - 4.4 ( -std=c++0x) dalam bentuk for(auto i=0, j=0.0; ..., tetapi kemungkinan ini dihapus dalam g ++ - 4.5 untuk bertepatan dengan teks c ++ 0x.
rafak

Jawaban:

133

C ++ 17 : Ya! Anda harus menggunakan deklarasi mengikat terstruktur . Sintaks telah didukung dalam gcc dan dentang selama bertahun-tahun (sejak gcc-7 dan dentang-4.0) ( contoh dentang hidup ). Ini memungkinkan kita untuk membongkar tuple seperti:

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

Di atas akan memberi Anda:

  • int i mulai 1
  • double f mulai 1.0
  • std::string s mulai "ab"

Pastikan #include <tuple>untuk deklarasi semacam ini.

Anda dapat menentukan jenis persis di dalam tupledengan mengetik semuanya seperti yang saya miliki dengan std::string, jika Anda ingin memberi nama jenis. Sebagai contoh:

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

Aplikasi spesifik ini adalah iterasi di atas peta, mendapatkan kunci dan nilai,

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

Lihat contoh langsung di sini


C ++ 14 : Anda dapat melakukan hal yang sama seperti C ++ 11 (di bawah) dengan penambahan berbasis tipe std::get. Jadi alih-alih std::get<0>(t)dalam contoh di bawah ini, Anda bisa memilikinya std::get<int>(t).


C ++ 11 : std::make_pairmemungkinkan Anda melakukan ini, juga std::make_tupleuntuk lebih dari dua objek.

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

std::make_pairakan mengembalikan dua argumen dalam std::pair. Elemen-elemen dapat diakses dengan .firstdan .second.

Untuk lebih dari dua objek, Anda harus menggunakan a 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_tupleadalah templat variadic yang akan membuat tuple dari sejumlah argumen (dengan beberapa batasan teknis tentunya). Elemen-elemen dapat diakses dengan indeks denganstd::get<INDEX>(tuple_object)

Di dalam for loop body Anda dapat dengan mudah alias objek, meskipun Anda masih perlu menggunakan .firstatau std::getuntuk kondisi loop dan memperbarui ekspresi

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 dan C ++ 03 Anda dapat secara eksplisit memberi nama jenis a std::pair. Tidak ada cara standar untuk menggeneralisasi ini ke lebih dari dua jenis:

for (std::pair<int, std::string> p(5, "Hello World"); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}
Ryan Haining
sumber
5
Jika Anda melakukan C ++ 17, Anda bahkan dapat menjatuhkan make_dan menulis std::pair(1, 1.0).
Marc Glisse
Bisnis tuple / pair gaya C ++ 14 yang berbulu - semuanya bagus (mungkin, terangkat), tetapi terlihat aneh :)
mlvljr
3
Singkatnya: Ya itu mungkin, tetapi tidak akan cantik.
Beberapa programmer Bung
Ya tidak cantik, tapi ini obat bius! Benar-benar menikmati yang tuple-ish. :) Tapi sebenarnya ini adalah kualitas sintaksis yang sangat tidak intuitif untuk loop di C ++ dan membuat saya sakit kepala selama lebih dari setengah jam untuk akhirnya menyadari apa yang harus di-Google-kan ...
aderchox
@aderchox jika Anda dapat memperjelas kesalahpahaman Anda, saya dapat memperbarui jawabannya
Ryan Haining
276

Tidak - tetapi secara teknis ada solusi (bukan berarti saya benar-benar menggunakannya kecuali dipaksa):

for(struct { int a; char b; } s = { 0, 'a' } ; s.a < 5 ; ++s.a) 
{
    std::cout << s.a << " " << s.b << std::endl;
}
Georg Fritzsche
sumber
3
Ini tidak dikompilasi pada VS 2008, tetapi pada Comeau online ;-)
JRL
7
@ JRL: Oh, VS2005 juga tidak. Namun fitur ketidakpatuhan lain di VC ++ kurasa.
Georg Fritzsche
3
Saya sudah melakukan yang setara di Perl. Saya belum mencoba menyelinap sesuatu seperti ini melalui review kode di C ++.
John
21
dengan c ++ 11 I Anda dapat membuat contoh ini lebih pendek menggunakan nilai defaultstruct { int a=0; char b='a'; } s;
Ryan Haining
1
Jawaban ini memenuhi persyaratan jawaban, tetapi dari keterbacaan POV saya lebih suka @MK. jawaban Solusi MK bahkan mengatasi pelingkupan dengan menambahkan kurung kurawal.
Trevor Boyd Smith
221

Tidak mungkin, tetapi Anda dapat melakukan:

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

Atau, secara eksplisit membatasi ruang lingkup fdan imenggunakan tanda kurung tambahan:

{
    float f; 
    int i;
    for (i = 0,f = 0.0; i < 5; i++)
    {
       //...
    }
}
MK.
sumber
Saya tahu ini adalah pertanyaan yang sangat lama, tetapi bisakah Anda menjelaskan mengapa beberapa orang melakukannya dengan tanda kurung tambahan di sekitarnya, seperti dalam contoh kedua Anda?
mengarungi
13
@fizzisist untuk secara eksplisit membatasi ruang lingkup f dan i hanya untuk bagian-bagian dari kode tempat mereka digunakan.
MK.
1
@MK. Terima kasih, itulah yang saya duga. Saya mengedit jawaban Anda untuk menjelaskannya.
ford
Hanya satu pertanyaan: Kenapa seperti ini? : O
rohan-patel
Karena berfungsi seperti 'int a = 0, b = 4', saya berasumsi. Yang sedang berkata, pelingkupan f dan i kemungkinan akan berguna hanya untuk mencegah penggunaan kembali nama-nama itu (yang merupakan alasan wajar), tetapi kode yang dihasilkan biasanya akan sama pada kompiler modern (dalam hal ini).
Asu
14

Anda tidak dapat mendeklarasikan beberapa tipe dalam inisialisasi, tetapi Anda dapat menetapkan beberapa tipe EG

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

Cukup deklarasikan mereka dalam ruang lingkup mereka sendiri.

zmbush
sumber
3

Saya pikir pendekatan terbaik adalah jawaban xian .

tapi...


# Bersarang untuk loop

Pendekatan ini kotor, tetapi bisa menyelesaikan semua versi.

jadi, saya sering menggunakannya di fungsi makro.

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
    }

Tambahan 1.

Ini juga dapat digunakan untuk declare local variablesdan 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.
    }

Tambahan 2.

Contoh bagus: dengan fungsi makro.

(Jika pendekatan terbaik tidak dapat digunakan karena itu adalah untuk-loop-makro)

#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
    }

# Trik If-statement

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

Jika Anda ingin menginisialisasi 0atau nullptr, Anda dapat menggunakan trik ini.

tetapi saya tidak merekomendasikan ini karena sulit membaca.

dan sepertinya bug.

mgcation
sumber
Tidak pernah berhenti membuat saya takjub betapa berbedanya beberapa orang dari orang lain. Saya tidak akan pernah memikirkan keanehan seperti itu. Ide yang menarik.
Dr. Person Person II
1

Lihat " Apakah ada cara untuk mendefinisikan variabel dari dua jenis dalam untuk loop? " Untuk cara lain yang melibatkan multiple bersarang untuk loop. Keuntungan dari cara lain selain "trik trik" Georg adalah bahwa itu (1) memungkinkan Anda untuk memiliki campuran variabel lokal statis dan non-statis dan (2) itu memungkinkan Anda untuk memiliki variabel yang tidak dapat disalin. The downside adalah bahwa itu jauh lebih mudah dibaca dan mungkin kurang efisien.

Goodgart
sumber
-2

Tentukan makro:

#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++)
{
  //...
}

Ingat saja, cakupan variabel Anda tidak akan berada dalam for loop dengan cara ini juga.

Ryan Favale
sumber
Anda dapat dengan mudah mengatasi batasan itu dengan membungkus kode di makro dalam lingkup terpisah menggunakan {dan }.
Nathan Osman
4
Tidak, dia tidak bisa. Makroya tidak membungkus loop body. Dia bisa menambahkan braket pembuka tambahan, tetapi itu akan membutuhkan braket penutup "ekstra" saat menggunakan makro.
John
3
Itu ide yang menarik, tetapi saya akan segera menggunakan jawaban lain sebelum mempertimbangkan ini.
gregn3
-2

Anda juga dapat menggunakan seperti di bawah ini di C ++.

int j=3;
int i=2;
for (; i<n && j<n ; j=j+2, i=i+2){
  // your code
}
loyola
sumber