2012-01-06 21 views
29

Hoặc là an toàn để sử dụng véc tơ nếu ĐTV của T chỉ liệt kê tất cả các phần tử?Có chuẩn C++ tương đương với IEnumerable <T> trong C# không?

+0

Có một điểm tương đương bạn có thể sử dụng trong C++ bạn sẽ quan tâm để xem mã ví dụ – MethodMan

+0

@DJKRAZE: Cảm ơn! Tôi chỉ cố gắng để xem nếu có cách tiếp cận thích hợp hơn khác hơn bằng cách sử dụng vector, có thể cho phép thực hiện riêng của chúng tôi GetEnumerator() trong C + + là tốt. – derekhh

Trả lời

45

Nó không phải là cần thiết trong C++ và đây là lý do tại sao:

C# chỉ hỗ trợ pol động ymorphism. Vì vậy, để tạo ra một thuật toán tái sử dụng, bạn cần một giao diện mà tất cả các trình vòng lặp sẽ thực hiện. Đó là IEnumerator<T>IEnumerable<T> là một nhà máy để trả lại một trình lặp.

Mặt khác, mẫu C++ hỗ trợ nhập vịt. Điều đó có nghĩa là bạn không cần phải ràng buộc một tham số kiểu generic bởi một giao diện để truy cập các thành viên - trình biên dịch sẽ tìm kiếm các thành viên theo tên cho mỗi sự khởi tạo riêng lẻ của khuôn mẫu.

C++ vùng chứa và trình lặp có giao diện ngầm tương đương với.NET IEnumerable<T>, IEnumerator<T>, ICollection<T>, IList<T>, cụ thể là:

Đối với container:

  • iteratorconst_iterator typedefs
  • chức năng begin() thành viên - lấp đầy nhu cầu chức năng IEnumerable<T>::GetEnumerator()
  • end() thành viên - thay vì của IEnumerator<T>::MoveNext() giá trị trả lại

Đối lặp về phía trước:

  • value_type typedef
  • operator++ - thay vì IEnumerator<T>::MoveNext()
  • operator*operator-> - thay vì IEnumerator<T>::Current
  • tham khảo kiểu trả về từ operator* - thay vì IList<T> indexer setter
  • operator==operator!= - không có tương đương đúng trong .NET, nhưng với container end() trận IEnumerator<T>::MoveNext() trở lại giá trị

Đối với vòng lặp truy cập ngẫu nhiên:

  • operator+, operator-, operator[] - thay vì IList<T>

Nếu bạn định nghĩa các thuật toán này, các thuật toán chuẩn sẽ hoạt động với vùng chứa và trình lặp của bạn. Không cần giao diện, không cần chức năng ảo. Không sử dụng các hàm ảo làm cho mã C++ chung nhanh hơn mã .NET tương đương, đôi khi nhanh hơn nhiều.


Lưu ý: khi viết các thuật toán chung chung, nó là tốt nhất để sử dụng std::begin(container)std::end(container) thay vì các hàm thành viên container. Điều đó cho phép thuật toán của bạn được sử dụng với các mảng thô (không có các hàm thành viên) ngoài các container STL. Các mảng thô và các con trỏ thô đáp ứng tất cả các yêu cầu khác của các thùng chứa và các trình vòng lặp, với ngoại lệ duy nhất này.

+3

Đây là một lời giải thích tốt cho việc tiêu thụ tương đương IEnumerable nhưng những gì về sản xuất chúng? Điều gì sẽ xảy ra nếu tôi muốn xác định một giao diện cho thấy thành viên tôi có thể bắt đầu() và kết thúc() nhưng không quan tâm đến loại cụ thể triển khai thành viên đó? – Sander

+1

Andrei Alexandrescu không đồng ý với bạn. Xem "Các bộ lặp lại phải đi": http://zao.se/~zao/boostcon/09/2009_presentations/wed/iterators-must-go.pdf C++/D Phạm vi là đề xuất thay thế và sẽ không bạn biết đấy, phạm vi khớp chính xác với giao diện .NET IEnumerator. – naasking

+1

@naasking: Tôi không biết cách cấu thành "bất đồng". Bây giờ chúng ta có hai cách để lặp lại các phạm vi mà không có một giao diện được gửi đi hầu như. Yêu cầu của bạn rằng phạm vi là 'IEnumerator' cho thấy sự thiếu hiểu biết của những gì' IEnumerator' thực sự là. Phạm vi là loại vịt, .NET 'IEnumerable' được tự động gửi đi. Và xin lưu ý rằng phạm vi, cho tất cả các lợi thế của họ, vẫn không phải là cách kinh điển để làm việc trong Standard C++. –

3

IEnumerable<T> là khái niệm rất khác với vector.

Các IEnumerable cung cấp về phía trước chỉ, read-only quyền truy cập vào một chuỗi các đối tượng, bất kể những gì container (nếu có) giữ các đối tượng. Một vector thực sự là một container chính nó.

Trong C++, nếu bạn muốn cung cấp quyền truy cập vào vùng chứa mà không cung cấp chi tiết của vùng chứa này, quy ước sẽ chuyển qua hai trình lặp đại diện cho phần đầu và cuối của vùng chứa.

Một ví dụ là C++ định nghĩa STL của accumulate, có thể được đối chiếu với IEnumerable<T>.Aggregate

Trong C++

int GetProduct(const vector<int>& v) 
    { 
     // We don't provide the container, but two iterators 
     return std::accumulate(v.begin(), v.end(), 1, multiplies<int>()); 
    } 

Trong C#

int GetProduct(IEnumerable<int> v) 
    { 
     v.Aggregate(1, (l, r) => l*r); 
    } 
+0

Câu trả lời này chỉ hiển thị mặt tiêu thụ và không hữu ích lắm. – BrainSlugs83

+1

@ BrainSlugs83: Nhận xét và ý kiến ​​của bạn không hữu ích lắm. Không có gì trong câu hỏi hỏi về cú pháp cú pháp cho việc thực thi trình lặp bằng cách sử dụng coroutines, đó chính xác là những gì mà C# 'yield return' là. Câu hỏi này không nhấn mạnh cả phía thực hiện hoặc bên tiêu thụ, nó hỏi về chính giao diện trình lặp và không phải trình bao bọc tiện lợi. Bạn đang áp đặt sự tò mò của riêng mình về việc triển khai, điều này là tự nhiên, nhưng không hữu ích. Nếu bạn muốn biết về chi tiết triển khai, hãy đặt câu hỏi của riêng bạn, đừng xâm nhập vào câu hỏi này. –

7

C++ chuẩn cách là phải vượt qua hai vòng lặp:

template<typename ForwardIterator> 
void some_function(ForwardIterator begin, ForwardIterator end) 
{ 
    for (; begin != end; ++begin) 
    { 
     do_something_with(*begin); 
    } 
} 

Ví dụ mã khách hàng:

std::vector<int> vec = {2, 3, 5, 7, 11, 13, 17, 19}; 
some_function(vec.begin(), vec.end()); 

std::list<int> lst = {2, 3, 5, 7, 11, 13, 17, 19}; 
some_function(lst.begin(), lst.end()); 

int arr[] = {2, 3, 5, 7, 11, 13, 17, 19}; 
some_function(arr + 0, arr + 8); 

Yay generic lập trình!

+0

cách tổng quát hơn là sử dụng các hàm miễn phí 'std :: begin' và' std :: end' – Abyx

+0

@Abyx: Mã máy khách đang hoạt động trên các kiểu không phụ thuộc có thể có '.begin' và' .end' để nó không có tỷ lệ cược ở đây. Trong mọi trường hợp, tính tổng quát của 'some_function' là điều quan trọng và sự lựa chọn trong mã máy khách không ảnh hưởng đến việc thực thi nó. –

+1

@Abyx: Mã máy khách chung? :) – fredoverflow

2

Nếu chúng tôi tuân theo câu hỏi, câu trả lời là không xa như tôi biết. Mọi người đã tiếp tục trả lời những gì là thay thế có sẵn trong C + +, mà có thể là thông tin tốt nhưng không phải câu trả lời, và mà Derekhh có lẽ hầu hết đã biết rồi.

Tôi hoàn toàn không đồng ý rằng "không cần thiết", nó chỉ là thiết kế của các thư viện chuẩn C++ và .NET khác nhau.Các tính năng chính của IEnumerable <> là nó đa hình, và vì vậy nó cho phép người gọi sử dụng bất cứ lớp nào anh ta muốn (mảng, Danh sách, Thiết lập vv), trong khi vẫn cung cấp thời gian biên dịch mạnh mẽ biên dịch, không an toàn ngay cả trong thư viện apis .

Lựa chọn duy nhất trong C++ là mẫu. Nhưng các mẫu C++ không được gõ một cách an toàn trong thời gian chạy, chúng về cơ bản là loại macro. Vì vậy, trước hết với các mẫu trong C++, bạn buộc phải cung cấp toàn bộ mã nguồn mẫu cho bất kỳ ai cần sử dụng mẫu của bạn. Hơn nữa nếu bạn làm cho thư viện của bạn api templated bạn mất khả năng đảm bảo rằng một cuộc gọi đến nó sẽ biên dịch, và mã không tự động tự tài liệu.

Tôi hoàn toàn thông cảm với bất kỳ lập trình viên nào khác sử dụng C# và C++ và thất vọng với điểm này. Theo tôi, các trình lặp std là thiết kế kém hơn so với các ý tưởng hiện đại hơn. Hiệu suất là hầu như không một đối số, nếu tôi muốn nó tôi luôn có thể sử dụng con trỏ thô; đặc biệt kể từ khi vòng lặp là ít hơn so với con trỏ bọc mỏng. Hãy thử de-referencing một iterator xảy ra trong thời gian chạy tới == .end() vì bạn quên kiểm tra, và bạn sẽ nhận được một tham chiếu không hợp lệ - mặc dù toàn bộ ý tưởng thiết kế đằng sau C++ rằng trước đây được cho là đảm bảo không bị vô hiệu hoặc không hợp lệ.

+0

"C++ templates không được gõ một cách an toàn thời gian chạy" là vô cùng gây hiểu nhầm. Chúng được gõ một cách an toàn (an toàn hơn nhiều so với .NET đa hình) biên dịch-thời gian (cho phép tối ưu hóa) generics. –

+0

Hãy để tôi thuật lại nó: C++ mẫu là nhưng macro. C++ dĩ nhiên là loại an toàn mạnh - giống như C#. –

+0

Mẫu C++ có sự khác biệt đáng kể về hành vi của các macro, cả hai loại macro yếu C và cũng có bất kỳ trình xử lý macro độc lập siêu mạnh nào mà bạn quan tâm. Các mẫu và hệ thống kiểu được gắn chặt với nhau. –

Các vấn đề liên quan