2012-08-17 22 views
155

Tôi tự hỏi tại sao cbegincend được giới thiệu trong C++ 11?Lý do đằng sau cbegin/cend là gì?

Những trường hợp nào khi gọi những phương pháp này tạo sự khác biệt giữa quá tải const của beginend?

Trả lời

179

Nó khá đơn giản. Giả sử tôi có vectơ:

std::vector<int> vec; 

Tôi điền vào một số dữ liệu. Sau đó, tôi muốn nhận được một số vòng lặp cho nó. Có thể vượt qua chúng xung quanh. Có lẽ để std::for_each:

std::for_each(vec.begin(), vec.end(), SomeFunctor()); 

Trong C++ 03, SomeFunctor được tự do để có thể sửa đổi tham số nó được. Chắc chắn, SomeFunctor có thể lấy thông số của nó theo giá trị hoặc bằng const&, nhưng không có cách nào để đảm bảo hoạt động. Không phải không có làm điều gì đó ngớ ngẩn như thế này:

const std::vector<int> &vec_ref = vec; 
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor()); 

Bây giờ, chúng tôi giới thiệu cbegin/cend:

std::for_each(vec.cbegin(), vec.cend(), SomeFunctor()); 

Bây giờ, chúng tôi có sự đảm bảo cú pháp mà SomeFunctor không thể sửa đổi các yếu tố của vector (mà không có một const đúc, tất nhiên). Chúng tôi rõ ràng nhận được const_iterator s và do đó SomeFunctor :: operator() sẽ được gọi với const int &. Nếu nó lấy tham số của nó là int &, C++ sẽ phát hành một lỗi trình biên dịch.


C++ 17 có giải pháp thanh lịch hơn cho vấn đề này: std::as_const. Vâng, ít nhất đó là thanh lịch khi sử dụng phạm vi dựa trên for:

for(auto &item : std::as_const(vec)) 

này chỉ đơn giản trả về một const& đến đối tượng nó được cung cấp.

+1

Tôi nghĩ giao thức mới là cbegin (vec) chứ không phải vec.cbegin(). –

+18

@Kaz: Không có các hàm miễn phí 'std :: cbegin/cend' theo cách' std :: begin/std :: end' tồn tại. Đó là sự giám sát của ủy ban. Nếu những chức năng đó tồn tại, thì đó sẽ là cách sử dụng chúng. –

+15

Rõ ràng, 'std :: cbegin/cend' sẽ được thêm vào trong C++ 14. Xem http://en.cppreference.com/w/cpp/iterator/begin –

8

Từ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf:

để một lập trình viên có thể trực tiếp lấy một const_iterator từ thậm chí một container không const

Họ cho ví dụ này

vector<MyType> v; 

// fill v ... 
typedef vector<MyType>::iterator iter; 
for(iter it = v.begin(); it != v.end(); ++it) { 
    // use *it ... 
} 

Tuy nhiên , khi quá trình truyền tải vùng chứa chỉ dành cho kiểm tra, nó là một thực tế thường ưa thích sử dụng một const_iterator để cho phép trình biên dịch để chẩn đoán vi phạm const-đúng đắn

Lưu ý rằng bản nghiên cứu cũng đề cập đến các mẫu adapter, mà bây giờ đã được hoàn thiện như std::begin()std::end() và cũng hoạt động với các mảng gốc. std::cbegin()std::cend() tương ứng bị thiếu nghiêm trọng vào thời điểm này, nhưng chúng cũng có thể được thêm vào.

60

Ngoài những gì Nicol Bolas nói trong his answer, hãy xem xét mới auto keyword:

auto iterator = container.begin(); 

Với auto, không có cách nào để đảm bảo rằng begin() trả về một nhà điều hành liên tục cho một tham chiếu chứa không liên tục. Vì vậy, bây giờ bạn làm:

auto const_iterator = container.cbegin(); 
+0

Bạn không thể làm const auto const_iterator = container.begin()? – allyourcode

+1

@allyourcode: Không hữu ích. Đối với trình biên dịch, 'const_iterator' chỉ là một định danh khác. Cả hai phiên bản đều sử dụng tra cứu của các thành viên thông thường typedefs 'decltype (container) :: iterator' hoặc' decltype (container) :: const_iterator'. – aschepler

+1

@aschepler Tôi không hiểu câu thứ hai của bạn, nhưng tôi nghĩ bạn đã bỏ lỡ "const" trước "tự động" trong câu hỏi của tôi. Bất cứ điều gì tự động đến, có vẻ như const_iterator nên const. – allyourcode

13

Hãy điều này như một usecase thực

void SomeClass::f(const vector<int>& a) { 
    auto it = someNonConstMemberVector.begin(); 
    ... 
    it = a.begin(); 
    ... 
} 

Việc chuyển nhượng thất bại vì it là một iterator nonconst. Nếu ban đầu bạn sử dụng cbegin, trình vòng lặp sẽ có kiểu đúng.

3

Chỉ cần stumbled khi câu hỏi này ... Tôi biết đó là alredy thưa: và nó chỉ là một nút bên ...

auto const it = container.begin() là một loại khác nhau sau đó auto it = container.cbegin()

sự khác biệt cho int[5] (sử dụng con trỏ, mà tôi biết không có sự bắt đầu phương pháp nhưng hiển thị độc đáo sự khác biệt ... nhưng sẽ làm việc trong C++ 14 cho std::cbegin()std::cend(), trong đó chủ yếu là những gì ta nên sử dụng khi nó ở đây) ...

int numbers = array[7]; 
const auto it = begin(numbers); // type is int* const -> pointer is const 
auto it = cbegin(numbers);  // type is int const* -> value is const 
1

iteratorconst_iterator có mối quan hệ thừa kế và chuyển đổi tiềm ẩn xảy ra khi so sánh hoặc được gán cho loại khác.

class T {} MyT1, MyT2, MyT3; 
std::vector<T> MyVector = {MyT1, MyT2, MyT3}; 
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it) 
{ 
    // ... 
} 

Sử dụng cbegin()cend() sẽ tăng hiệu suất trong trường hợp này.

for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it) 
{ 
    // ... 
}