Một lợi ích của std::begin
và std::end
là chúng đóng vai trò là điểm mở rộng để thực hiện giao diện chuẩn cho các lớp bên ngoài.
Nếu bạn muốn sử dụng CustomContainer
lớp với hàm vòng lặp hoặc hàm mẫu dựa trên phạm vi mong đợi .begin()
và .end()
phương thức, rõ ràng bạn phải thực hiện các phương thức đó.
Nếu lớp học cung cấp các phương thức đó, đó không phải là vấn đề. Khi không, bạn phải sửa đổi nó *.
Điều này không phải lúc nào cũng khả thi, ví dụ như khi sử dụng thư viện bên ngoài, đặc biệt là nguồn thương mại và nguồn đóng.
Trong các tình huống như vậy, std::begin
và std::end
có ích, vì người ta có thể cung cấp API lặp mà không cần sửa đổi chính lớp đó, mà thay vào đó là quá tải các hàm miễn phí.
Ví dụ: giả sử rằng bạn muốn triển khai count_if
hàm lấy một container thay vì một cặp trình vòng lặp. Mã như vậy có thể trông như thế này:
template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
using std::begin;
using std::end;
return std::count_if(begin(container), end(container),
std::forward<PredicateType&&>(predicate));
}
Bây giờ, đối với bất kỳ lớp nào bạn muốn sử dụng với tùy chỉnh này count_if
, bạn chỉ phải thêm hai hàm miễn phí, thay vì sửa đổi các lớp đó.
Bây giờ, C ++ có một cơ chế gọi là Tra cứu phụ thuộc đối số
(ADL), giúp cho cách tiếp cận như vậy thậm chí linh hoạt hơn.
Nói tóm lại, ADL có nghĩa là, khi trình biên dịch giải quyết một hàm không đủ tiêu chuẩn (nghĩa là hàm không có không gian tên, như begin
thay vì std::begin
), nó cũng sẽ xem xét các hàm được khai báo trong không gian tên của các đối số của nó. Ví dụ:
namesapce some_lib
{
// let's assume that CustomContainer stores elements sequentially,
// and has data() and size() methods, but not begin() and end() methods:
class CustomContainer
{
...
};
}
namespace some_lib
{
const Element* begin(const CustomContainer& c)
{
return c.data();
}
const Element* end(const CustomContainer& c)
{
return c.data() + c.size();
}
}
// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);
Trong trường hợp này, nó không quan trọng mà tên tiêu chuẩn là some_lib::begin
và some_lib::end
- kể từ khi CustomContainer
đang trong some_lib::
quá, trình biên dịch sẽ sử dụng những quá tải trong count_if
.
Đó cũng là lý do để có using std::begin;
và using std::end;
trong count_if
. Điều này cho phép chúng tôi sử dụng không đủ tiêu chuẩn begin
và end
do đó cho phép ADL và
cho phép trình biên dịch chọn std::begin
và std::end
khi không tìm thấy giải pháp thay thế nào khác.
Chúng ta có thể ăn cookie và có cookie - tức là có cách cung cấp tùy chỉnh thực hiện begin
/ end
trong khi trình biên dịch có thể quay lại tiêu chuẩn.
Một số lưu ý:
Vì lý do tương tự, có các hàm tương tự khác: std::rbegin
/ rend
,
std::size
và std::data
.
Như các câu trả lời khác đề cập, std::
các phiên bản có quá tải cho mảng trần. Điều đó hữu ích, nhưng chỉ đơn giản là một trường hợp đặc biệt của những gì tôi đã mô tả ở trên.
Sử dụng std::begin
và bạn bè là ý tưởng đặc biệt tốt khi viết mã mẫu, bởi vì điều này làm cho các mẫu đó chung chung hơn. Đối với phi mẫu, bạn cũng có thể sử dụng các phương thức, khi áp dụng.
PS Tôi biết rằng bài này đã gần 7 tuổi. Tôi đã xem qua nó vì tôi muốn trả lời một câu hỏi được đánh dấu là trùng lặp và phát hiện ra rằng không có câu trả lời nào ở đây đề cập đến ADL.