เป็นไปได้ไหมที่จะประกาศสองตัวแปรของชนิดที่แตกต่างกันใน for for loop


240

เป็นไปได้ไหมที่จะประกาศสองตัวแปรที่มีชนิดต่างกันในเนื้อความการเริ่มต้นของ for for loop ใน C ++?

ตัวอย่างเช่น:

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

กำหนดจำนวนเต็มสองจำนวน ฉันสามารถนิยามinta และ a 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 และเสียงดังกราวเป็นเวลาหลายปี (ตั้งแต่ gcc-7 และ clang-4.0) ( ตัวอย่างเสียงดังกราวสด ) สิ่งนี้ช่วยให้เราสามารถคลายสิ่งอันดับเป็น:

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_pairstd::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เป็นเทมเพลต Variadic ที่จะสร้าง tuple ของจำนวนอาร์กิวเมนต์ใด ๆ (โดยมีข้อ จำกัด ทางเทคนิคบางประการ) องค์ประกอบสามารถเข้าถึงได้โดยดัชนีด้วยstd::get<INDEX>(tuple_object)

ภายใน for loop body คุณสามารถนามแฝงวัตถุได้อย่างง่ายดายแม้ว่าคุณจะต้องใช้.firstหรือstd::getสำหรับเงื่อนไข loop และ update expression

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)ได้
Marc Glisse

ธุรกิจขนปุย C ++ 14 สไตล์คู่ / - ดี (อาจ upvoted) ทั้งหมด แต่ดูแปลกประหลาด :)
mlvljr

3
กล่าวโดยย่อ: ใช่เป็นไปได้ แต่จะไม่สวย
โปรแกรมเมอร์บางคนเพื่อน

ใช่ไม่สวย แต่ก็เป็นยาเสพติด! มีความสุขกับ tuple-ish อย่างแน่นอน :) แต่จริงๆมันเป็นประโยคที่มีคุณภาพ unintuitive มากสำหรับลูปใน C ++ และให้ฉันปวดหัวมานานกว่าครึ่งชั่วโมงไปจนตระหนักถึงสิ่งที่จะต้อง Googled ...
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 ++ ฉันเดาว่า
Georg Fritzsche

3
ฉันได้ทำเทียบเท่าใน Perl ฉันไม่ได้ลองอะไรแบบนี้ผ่านการตรวจสอบโค้ดใน C ++
John

21
ด้วย c ++ 11 ฉันสามารถทำให้ตัวอย่างนี้สั้นลงโดยใช้ค่าเริ่มต้นstruct { int a=0; char b='a'; } s;
Ryan Haining

1
คำตอบนี้ตอบสนองความต้องการของคำตอบ แต่จาก POV ที่สามารถอ่านได้ฉันชอบ @MK คำตอบของ วิธีการแก้ปัญหาของ MK ยังระบุถึงการกำหนดขอบเขตด้วยการเพิ่มเครื่องหมายปีกกา
Trevor Boyd Smith

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 ไว้อย่างชัดเจนเฉพาะบางส่วนของรหัสที่ใช้
MK

1
@MK ขอบคุณนั่นคือสิ่งที่ฉันสงสัย ฉันแก้ไขคำตอบของคุณเพื่ออธิบายว่า
ฟอร์ด

คำถามเดียวเท่านั้น: ทำไมเช่นนี้ : O
rohan-patel

เพราะมันใช้งานได้เหมือน 'int a = 0, b = 4' ฉันถือว่า ที่ถูกกล่าวว่าการกำหนดขอบเขต f และ i น่าจะมีประโยชน์เพียงเพื่อป้องกันการนำชื่อเหล่านั้นกลับมาใช้ใหม่ (ซึ่งเป็นเหตุผลที่ยุติธรรม) แต่รหัสที่สร้างขึ้นจะเหมือนกันในคอมไพเลอร์สมัยใหม่ (ในกรณีนี้)
Asu

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 variablesinitialize 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

ตัวอย่างที่ดี: ด้วยฟังก์ชั่นมาโคร

(ถ้าวิธีที่ดีที่สุดไม่สามารถใช้ได้เพราะมันเป็น for-loop-macro)

#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คุณสามารถใช้เคล็ดลับนี้

แต่ฉันไม่แนะนำเพราะอ่านยาก

และดูเหมือนว่าข้อผิดพลาด


ไม่เคยทำให้ฉันประหลาดใจเลยว่าบางคนคิดแตกต่างจากคนอื่นอย่างไร ฉันไม่เคยคิดเรื่องแปลก ๆ ความคิดที่น่าสนใจ
Dr. Person Person II II

1

ดูที่ " มีวิธีกำหนดตัวแปรสองชนิดสำหรับลูปหรือไม่ " สำหรับวิธีอื่นที่เกี่ยวข้องกับการซ้อนซ้อนกันหลายครั้งสำหรับลูป ข้อดีของวิธีอื่น ๆ ในการใช้ "เคล็ดลับ struct" ของ Georg ก็คือมัน (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++)
{
  //...
}

เพียงจำไว้ว่าขอบเขตตัวแปรของคุณจะไม่อยู่ในการวนซ้ำด้วยวิธีนี้เช่นกัน


คุณสามารถจะเอาชนะข้อ จำกัด ว่าด้วยการตัดรหัสในแมโครในขอบเขตเฉพาะกิจการและ{ }
นาธานออสมัน

4
ไม่เขาทำไม่ได้ มาโครของเขาไม่ห่อหุ้มลูป เขาสามารถเพิ่มวงเล็บเปิดตัวพิเศษ แต่นั่นจะต้องมีวงเล็บปิด "พิเศษ" เมื่อใช้แมโคร
John

3
มันเป็นแนวคิดที่น่าสนใจ แต่ฉันจะใช้คำตอบอื่นใดก่อนที่จะพิจารณาสิ่งนี้
gregn3

-2

นอกจากนี้คุณสามารถใช้เช่นด้านล่างใน C ++

int j=3;
int i=2;
for (; i<n && j<n ; j=j+2, i=i+2){
  // your code
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.