Có thể khai báo hai biến có kiểu khác nhau trong một vòng lặp for không?


240

Có thể khai báo hai biến có kiểu khác nhau trong phần khởi tạo của vòng lặp for trong C ++ không?

Ví dụ:

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

định nghĩa hai số nguyên. Tôi có thể định nghĩa một intvà một chartrong cơ thể khởi tạo không? Làm thế nào điều này sẽ được thực hiện?


3
Có thể ở g ++ - 4.4 ( -std=c++0x) ở dạng for(auto i=0, j=0.0; ..., nhưng khả năng này đã bị loại bỏ trong g ++ - 4.5 trùng với các văn bản c ++ 0x.
rafak

Câu trả lời:


133

C ++ 17 : Vâng! Bạn nên sử dụng một tuyên bố ràng buộc có cấu trúc . Cú pháp đã được hỗ trợ trong gcc và clang trong nhiều năm (kể từ gcc-7 và clang-4.0) ( ví dụ trực tiếp về tiếng kêu ). Điều này cho phép chúng tôi giải nén một tuple như vậy:

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

Ở trên sẽ cung cấp cho bạn:

  • int i đặt thành 1
  • double f đặt thành 1.0
  • std::string s đặt thành "ab"

Hãy chắc chắn để #include <tuple>loại khai báo này.

Bạn có thể chỉ định các loại chính xác bên trong tuplebằng cách nhập tất cả các loại như tôi có std::string, nếu bạn muốn đặt tên cho một loại. Ví dụ:

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

Một ứng dụng cụ thể của điều này là lặp lại trên bản đồ, lấy khóa và giá trị,

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

Xem ví dụ trực tiếp tại đây


C ++ 14 : Bạn có thể làm tương tự như C ++ 11 (bên dưới) với việc bổ sung dựa trên kiểu std::get. Vì vậy, thay vì std::get<0>(t)trong ví dụ dưới đây, bạn có thể có std::get<int>(t).


C ++ 11 : std::make_paircho phép bạn làm điều này, cũng như std::make_tuplecho nhiều hơn hai đối tượng.

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

std::make_pairsẽ trả về hai đối số trong a std::pair. Các yếu tố có thể được truy cập với .first.second.

Đối với nhiều hơn hai đối tượng, bạn sẽ cần sử dụng một 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_tuplelà một khuôn mẫu có tính đột biến sẽ tạo ra một tuple của bất kỳ số lượng đối số nào (với một số hạn chế về kỹ thuật). Các yếu tố có thể được truy cập bởi chỉ mục vớistd::get<INDEX>(tuple_object)

Trong các thân vòng lặp for, bạn có thể dễ dàng đặt bí danh cho các đối tượng, mặc dù bạn vẫn cần sử dụng .firsthoặc std::getcho điều kiện vòng lặp for và biểu thức cập nhật

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 và C ++ 03 Bạn có thể đặt tên rõ ràng cho các loại a std::pair. Không có cách tiêu chuẩn để khái quát hóa điều này thành nhiều hơn hai loại:

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

5
Nếu bạn đang làm C ++ 17, bạn thậm chí có thể bỏ make_và viết std::pair(1, 1.0).
Marc Glisse

Doanh nghiệp tuple / cặp kiểu C ++ 14 đầy lông - tất cả đều tốt (có thể, được nâng cấp), nhưng trông kỳ quái :)
mlvljr

3
Tóm lại: Có, điều đó là có thể, nhưng sẽ không đẹp.
Một số lập trình viên anh chàng

Vâng không đẹp, nhưng đó là dope! Hoàn toàn thích một tuple-ish. :) Nhưng thực sự đó là chất lượng cú pháp rất không trực quan đối với các vòng lặp trong C ++ và khiến tôi đau đầu hơn nửa giờ để cuối cùng nhận ra điều gì đã bị Googled ...
aderchox 16/12/19

@aderchox nếu bạn có thể làm rõ sự hiểu lầm của mình, tôi có thể cập nhật câu trả lời
Ryan

276

Không - nhưng về mặt kỹ thuật có một cách giải quyết (không phải là tôi thực sự sẽ sử dụng nó trừ khi bị ép buộc):

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

3
Điều này không biên dịch trên VS 2008, nhưng trên Comeau trực tuyến ;-)
JRL

7
@JRL: Ồ, VS2005 cũng không. Một tính năng không tuân thủ khác trong VC ++ tôi đoán.
Georg Fritzsche

3
Tôi đã thực hiện tương đương trong Perl. Mặc dù vậy, tôi đã không cố gắng lén lút một cái gì đó như thế này thông qua đánh giá mã trong C ++.
Giăng

21
với c ++ 11 Tôi có thể rút ngắn ví dụ này bằng các giá trị mặc địnhstruct { int a=0; char b='a'; } s;
Ryan Hained

1
Câu trả lời này đáp ứng các yêu cầu của câu trả lời, nhưng từ POV dễ đọc, tôi thích @MK. Câu trả lời. Giải pháp của MK thậm chí còn giải quyết phạm vi bằng cách thêm dấu ngoặc nhọn.
Trevor Boyd Smith

221

Không thể, nhưng bạn có thể làm:

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

Hoặc, giới hạn rõ ràng phạm vi fisử dụng dấu ngoặc bổ sung:

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

Tôi biết đây là một câu hỏi rất cũ, nhưng bạn có thể giải thích tại sao một số người sẽ làm điều đó với các dấu ngoặc phụ xung quanh nó, như trong ví dụ thứ hai của bạn không?
ford

13
@fizzisist giới hạn rõ ràng phạm vi của f và i chỉ các phần của mã nơi chúng được sử dụng.
MK.

1
@MK. Cảm ơn, đó là những gì tôi nghi ngờ. Tôi chỉnh sửa câu trả lời của bạn để giải thích điều đó.
ford

Chỉ có một câu hỏi: Tại sao như thế này? : O
rohan-patel

Bởi vì nó hoạt động như 'int a = 0, b = 4', tôi giả sử. Điều đó đang được nói, phạm vi f và tôi có thể sẽ chỉ hữu ích để ngăn chặn việc sử dụng lại các tên đó (đó là một lý do hợp lý), nhưng mã được tạo thường sẽ giống nhau trên một trình biên dịch hiện đại (trong trường hợp này).
Asu

14

Bạn không thể khai báo nhiều loại trong quá trình khởi tạo, nhưng bạn có thể gán cho nhiều loại EG

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

Chỉ cần tuyên bố chúng trong phạm vi riêng của họ.


3

Tôi nghĩ cách tiếp cận tốt nhất là câu trả lời của xian .

nhưng...


# Lồng cho vòng lặp

Cách tiếp cận này là bẩn, nhưng có thể giải quyết ở tất cả các phiên bản.

vì vậy, tôi thường sử dụng nó trong các chức năng macro.

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
    }

Bổ sung 1.

Nó cũng có thể được sử dụng để 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.
    }

Bổ sung 2.

Ví dụ hay: với chức năng macro.

(Nếu cách tiếp cận tốt nhất không thể được sử dụng vì đó là macro 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
    }

# Thủ thuật if-statement

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

Nếu bạn muốn khởi tạo 0hoặc nullptr, bạn có thể sử dụng thủ thuật này.

nhưng tôi không khuyến khích điều này vì khó đọc.

và nó có vẻ như lỗi.


Nó không bao giờ hết làm tôi ngạc nhiên khi một số người khác với những người khác. Tôi sẽ không bao giờ nghĩ về những điều kỳ lạ như vậy. Ý tưởng thú vị.
Tiến sĩ Person Person II

1

Xem " Có cách nào để xác định các biến của hai loại trong vòng lặp không? " Cho một cách khác liên quan đến việc lồng nhiều vòng cho các vòng lặp. Ưu điểm của cách khác so với "struct trick" của Georg là nó (1) cho phép bạn có một hỗn hợp các biến cục bộ tĩnh và không tĩnh và (2) nó cho phép bạn có các biến không thể sao chép. Nhược điểm là nó ít đọc hơn và có thể kém hiệu quả hơn.


-2

Xác định một macro:

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

Chỉ cần nhớ, phạm vi biến của bạn cũng sẽ không nằm trong vòng lặp for.


Bạn có thể dễ dàng khắc phục giới hạn đó bằng cách gói mã trong macro trong một phạm vi riêng bằng cách sử dụng {}.
Nathan Osman

4
Không, anh không thể. Macro của anh ấy không bao bọc cơ thể vòng lặp. Anh ta có thể thêm một khung mở thêm, nhưng điều đó sẽ yêu cầu một khung đóng "phụ" khi sử dụng macro.
Giăng

3
Đó là một ý tưởng thú vị, nhưng tôi sẽ sớm sử dụng bất kỳ câu trả lời nào khác trước khi xem xét điều này.
gregn3

-2

Ngoài ra, bạn có thể sử dụng như dưới đây trong C ++.

int j=3;
int i=2;
for (; i<n && j<n ; j=j+2, i=i+2){
  // your code
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.