2015-06-03 14 views
6

Tôi đã cố gắng in một mảng 2D bằng cách sử dụng for_each và phạm vi dựa trên vòng lặp for.for_each & rangeed base cho trên mảng 2D

chương trình của tôi đi như thế này: -

#include <iostream> 
#include <algorithm> 
using namespace std; 
int main() 
{ 
    int a[3][3]={{1,2,3},{4,5,6},{7,8,9}}; 
    //for_each (begin(a), end(a), [] (int x) { cout<<x<<" ";}); this code throws error 

    for_each (begin(a[0]), end(a[2]), [] (int x) { cout<<x<<" ";}); //this code works well, why ? 

    cout<<endl; 

    for (auto &row: a) // without & for row, error is thrown 
    { 
    for (auto x:row) // no & needed for x, why ? 
    { 
     cout<<x<<" "; 
    } 
    } 
    return 0; 
} 

Tại sao for_each lỗi ném đầu tiên của tôi và tại sao & biểu tượng cần thiết cho hàng? Loại của nó là gì? Có phải là row một con trỏ không?

Trả lời

4
for_each (begin(a), end(a), [] (int x) { cout<<x<<" ";}); 

begin(a) mang lại một int(*)[3] (con trỏ đến mảng có kích thước [3]), và dereferencing nó mang lại một int(&)[3], trong khi biểu thức lambda của bạn hy vọng một cuộc tranh cãi int.

for_each (begin(a[0]), end(a[2]), [] (int x) { cout<<x<<" ";}); 

begin(a[0]) mang lại một int* trỏ đến phần tử đầu tiên trong hàng đầu tiên của a, và end(a[2]) mang lại một trỏ int* đến một quá khứ yếu tố cuối cùng ở hàng cuối cùng của a, vì vậy mọi thứ hoạt động.


Bây giờ cho phạm vi dựa trên for.

Nếu bạn xóa & khỏi dòng for (auto& row : a), lỗi thực sự xảy ra trên dòng sau for(auto x : row). Điều này là do cách thức range-based for được chỉ định. Mệnh đề thích hợp với trường hợp sử dụng của bạn là

Nếu __range là một mảng, sau đó begin_expr được __rangeend_expr(__range + __bound), nơi __bound là số phần tử trong mảng (nếu mảng có kích thước chưa biết hay là của một loại không đầy đủ, chương trình là vô hình thành)

hereon tôi sẽ đề cập đến định danh được nhắc đến trong Giải thích phần của trang được liên kết.


Hãy xem xét các trường hợp for (auto& row : a):

__range là suy luận như int(&)[3][3] (tham chiếu đến mảng có kích thước [3] [3]). __begin sau đó được suy ra dưới dạng int(*)[3] (trỏ đến mảng có kích thước [3]) vì loại phân đoạn __range thành con trỏ đến hàng đầu tiên của mảng 2D. range_expression bạn có là auto& row, vì vậy row được suy ra là int(&)[3] (tham chiếu đến mảng kích thước [3]).

Tiếp theo, quá trình tương tự được lặp lại cho phạm vi bên trong dựa trên for. Trong trường hợp này, __rangeint(&)[3] và điều khoản mảng tôi trích dẫn ở trên sẽ được áp dụng; quá trình khấu trừ loại còn lại tương tự như những gì tôi đã mô tả ở trên.

__range = int(&)[3] 
__begin = int* 
x  = int 

Bây giờ xem xét các trường hợp for (auto row : a):

__range, __begin__end đều suy luận như vậy. Sự khác biệt quan trọng trong trường hợp này là range_expressionauto row, gây ra sự phân rã của loại int(*)[3]__begin được suy ra dưới dạng. Điều này có nghĩa là row được suy ra là int * và không có mệnh đề nào trong số 3 mệnh đề xác định begin_expr/end_expr được mô tả xử lý con trỏ thô. Điều này dẫn đến lỗi biên dịch trong vòng lặp lồng nhau for.

+0

Thnx boss! Lời giải thích này rất rõ ràng và ngắn gọn! – Anwesha

3

a là mảng hai chiều - int[][] nếu bạn muốn.

Điều này có nghĩa là khi bạn lặp lại a, bạn chỉ đang lặp lại thứ nguyên mảng đầu tiên - a[0] đến a[2]. a[0] vẫn là một mảng, điều này giải thích tại sao for_each đầu tiên của bạn có thể gây ra lỗi - lambda mà bạn cung cấp đang mong đợi một số int nhưng sẽ được chuyển qua số int*.

Tham chiếu (& biểu tượng) là cần thiết trong phạm vi đầu tiên dựa trên vì cùng một lý do. Nếu không có & trình biên dịch sẽ cố gắng sao chép theo giá trị các mục trong thứ nguyên đầu tiên, nhưng đây là các mảng, và bạn không thể sao chép một mảng theo giá trị trong C++.

Phạm vi thứ hai dựa trên không cần tham chiếu vì nó đang lặp thứ nguyên mảng thứ hai, bao gồm int đơn giản.