Câu trả lời:
Nó khá đơn giản. Nói rằng tôi có một vectơ:
std::vector<int> vec;
Tôi điền nó với một số dữ liệu. Sau đó, tôi muốn có được một số vòng lặp cho nó. Có lẽ vượt qua chúng xung quanh. Có thể để std::for_each
:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
Trong C ++ 03, SomeFunctor
đã có thể tự do sửa đổi tham số mà nó nhận được. Chắc chắn, SomeFunctor
có thể lấy tham số của nó theo giá trị hoặc theo const&
, nhưng không có cách nào để đảm bảo rằng nó làm được. Không phải không 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 rằng cú pháp SomeFunctor
không thể sửa đổi các yếu tố của vector (mà không có một const-cast, 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 sinh lỗi trình biên dịch.
C ++ 17 có một giải pháp thanh lịch hơn cho vấn đề này : std::as_const
. Chà, ít nhất là nó thanh lịch khi sử dụng dựa trên phạm vi for
:
for(auto &item : std::as_const(vec))
Điều này chỉ đơn giản trả về một const&
đối tượng mà nó được cung cấp.
std::cbegin/cend
chức năng miễn phí theo cách std::begin/std::end
tồn tại. Đó là một sự giám sát của ủy ban. Nếu những chức năng đó tồn tại, đó thường sẽ là cách sử dụng chúng.
std::cbegin/cend
sẽ được thêm vào trong C ++ 14. Xem en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))
tương đương với for(const auto &item : vec)
?
const
vào tham chiếu. Nicol's xem container là const, do đó auto
suy ra một const
tài liệu tham khảo. IMO auto const& item
dễ dàng và rõ ràng hơn. Không rõ tại sao lại std::as_const()
tốt ở đây; Tôi có thể thấy nó sẽ hữu ích khi chuyển một const
mã không chung sang mã chung mà chúng ta không thể kiểm soát loại được sử dụng, nhưng với phạm vi- for
, chúng ta có thể, vì vậy nó giống như thêm tiếng ồn cho tôi ở đó.
Ngoài những gì Nicol Bolas đã nói trong câu trả lời của mình , hãy xem xét auto
từ khóa mới :
auto iterator = container.begin();
Với auto
, không có cách nào để đảm bảo rằng begin()
trả về một toán tử không đổi cho tham chiếu vùng chứa không liên tục. Vì vậy, bây giờ bạn làm:
auto const_iterator = container.cbegin();
const_iterator
chỉ là một định danh khác. Cả hai phiên bản đều không sử dụng tra cứu các typedefs thông thường decltype(container)::iterator
hoặc decltype(container)::const_iterator
.
const_iterator
với auto
: Viết một mẫu chức năng phụ trợ gọi là make_const
để đủ điều kiện tham số đối tượng.
Hãy coi đây là một usecase thực tế
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 trình lặp không lặp. Nếu bạn sử dụng cbegin ban đầu, iterator sẽ có đúng loại.
Từ http://www.open-std.org/jtc1/sc22/wg21/docs/ con / 2004 / n1674.pdf :
để một lập trình viên có thể trực tiếp lấy const_iterator từ ngay cả một thùng chứa không phải const
Họ đã đưa ra 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 truyền tải container chỉ nhằm mục đích kiểm tra, việc sử dụng const_iterator để cho phép trình biên dịch chẩn đoán vi phạm const-orthity là một cách phổ biến.
Lưu ý rằng bản nghiên cứu cũng đề cập đến bộ chuyển đổi mẫu, mà bây giờ đã được hoàn thiện như std::begin()
và std::end()
và đó cũng làm việc với mảng bản địa. Tương ứng std::cbegin()
và std::cend()
bị mất tích một cách tò mò vào thời điểm này, nhưng chúng cũng có thể được thêm vào.
Chỉ vấp phải câu hỏi này ... Tôi biết đó là câu trả lời tuyệt vời và nó chỉ là một nút bên ...
auto const it = container.begin()
là một loại khác nhau 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ó phương thức bắt đầu nhưng chỉ ra sự khác biệt ... nhưng sẽ hoạt động trong c ++ 14 std::cbegin()
và std::cend()
, về cơ bản là những gì người ta nên sử dụng khi ở đâ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
iterator
và const_iterator
có mối quan hệ thừa kế và một chuyển đổi ngầm xảy ra khi so sánh với 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()
và 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)
{
// ...
}
const
lợi ích chính là hiệu suất (không phải là: mã chính xác và an toàn về mặt ngữ nghĩa). Nhưng, trong khi bạn có một điểm, (A) auto
làm cho điều đó không thành vấn đề; (B) khi nói về hiệu suất, bạn đã bỏ lỡ một điều chính bạn nên làm ở đây: lưu trữ bộ end
lặp bằng cách khai báo một bản sao của nó trong điều kiện ban đầu của for
vòng lặp, và so sánh với điều đó, thay vì nhận một bản sao mới bằng giá trị cho mỗi lần lặp. Điều đó sẽ làm cho quan điểm của bạn tốt hơn. : P
const
chắc chắn có thể giúp đạt được hiệu suất tốt hơn, không phải vì một số phép thuật trong const
chính từ khóa mà bởi vì trình biên dịch có thể kích hoạt một số tối ưu hóa nếu biết rằng dữ liệu sẽ không được sửa đổi, điều này sẽ không thể xảy ra. Kiểm tra bit này từ bài nói chuyện của Jason Turner để biết ví dụ trực tiếp về điều này.
const
có thể (gần như gián tiếp) dẫn đến lợi ích hiệu suất; chỉ trong trường hợp ai đó đang đọc điều này có thể nghĩ rằng "Tôi sẽ không bận tâm thêm const
nếu mã được tạo không bị ảnh hưởng theo bất kỳ cách nào", điều đó không đúng.
đơn giản, cbegin trả về một trình vòng lặp không đổi trong đó bắt đầu trả về chỉ là một trình vòng lặp
để hiểu rõ hơn, hãy lấy hai kịch bản ở đây
cảnh 1 :
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.begin();i< v.end();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
Điều này sẽ chạy vì ở đây iterator i không phải là hằng số và có thể tăng thêm 5
bây giờ, hãy sử dụng cbegin và ký hiệu biểu thị chúng dưới dạng kịch bản lặp không đổi - 2:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.cbegin();i< v.cend();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
điều này sẽ không hoạt động, bởi vì bạn không thể cập nhật giá trị bằng cách sử dụng cbegin và cend trả về trình lặp không đổi