Tiêu chuẩn đã được thay đổi kể từ khi câu hỏi (và hầu hết các câu trả lời) được đăng trong độ phân giải của báo cáo lỗi này .
Cách để tạo một for(:)
vòng lặp hoạt động trên loại của bạn X
bây giờ là một trong hai cách:
Tạo thành viên X::begin()
và X::end()
trả về một cái gì đó hoạt động như một trình vòng lặp
Tạo một hàm miễn phí begin(X&)
và end(X&)
trả về một cái gì đó hoạt động như một trình vòng lặp, trong cùng một không gian tên với kiểu của bạn X
.¹
Và tương tự cho const
các biến thể. Điều này sẽ làm việc cả trên các trình biên dịch thực hiện các thay đổi báo cáo lỗi và các trình biên dịch không.
Các đối tượng trả về không phải thực sự là các vòng lặp. Các for(:)
vòng lặp, không giống như hầu hết các bộ phận của C ++ chuẩn, được quy định để mở rộng đến một cái gì đó tương đương với :
for( range_declaration : range_expression )
trở thành:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
trong đó các biến bắt đầu __
chỉ để giải thích begin_expr
và end_expr
là phép thuật gọi begin
/ end
.²
Các yêu cầu về giá trị trả về bắt đầu / kết thúc rất đơn giản: Bạn phải quá tải trước ++
, đảm bảo các biểu thức khởi tạo là hợp lệ, nhị phân !=
có thể được sử dụng trong ngữ cảnh boolean, unary *
trả về một cái gì đó bạn có thể gán - khởi tạo range_declaration
và hiển thị công khai phá hủy.
Làm như vậy theo cách không tương thích với iterator có lẽ là một ý tưởng tồi, vì các lần lặp lại trong tương lai của C ++ có thể tương đối khó khăn hơn về việc phá mã của bạn nếu bạn làm như vậy.
Bên cạnh đó, rất có khả năng một bản sửa đổi tương lai của tiêu chuẩn sẽ cho phép end_expr
trả lại một loại khác hơn begin_expr
. Điều này hữu ích ở chỗ nó cho phép đánh giá "kết thúc lười biếng" (như phát hiện chấm dứt null) dễ dàng tối ưu hóa để có hiệu quả như vòng lặp C viết tay và các ưu điểm tương tự khác.
Lưu ý rằng for(:)
các vòng lặp lưu trữ bất kỳ tạm thời nào trong một auto&&
biến và chuyển nó cho bạn dưới dạng giá trị. Bạn không thể phát hiện nếu bạn đang lặp lại một giá trị tạm thời (hoặc giá trị khác); quá tải như vậy sẽ không được gọi bởi một for(:)
vòng lặp. Xem [stmt. Sắp xếp] 1.2-1.3 từ n4527.
² Hoặc gọi begin
/ end
phương pháp, hoặc ADL chỉ tra cứu miễn phí chức năng begin
/ end
, hoặc ma thuật để hỗ trợ mảng C-phong cách. Lưu ý rằng std::begin
không được gọi trừ khi range_expression
trả về một đối tượng thuộc loại namespace std
hoặc phụ thuộc vào cùng.
Trong c ++ 17 biểu thức cho phạm vi đã được cập nhật
{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr;
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
với các loại __begin
và __end
đã được tách rời.
Điều này cho phép iterator kết thúc không cùng loại với bắt đầu. Loại trình lặp cuối của bạn có thể là "sentinel" chỉ hỗ trợ !=
với loại trình lặp bắt đầu.
Một ví dụ thực tế về lý do tại sao điều này hữu ích là trình lặp cuối của bạn có thể đọc "kiểm tra xem bạn char*
có xem nó '0'
" khi ==
với a char*
. Điều này cho phép một biểu thức phạm vi C ++ tạo mã tối ưu khi lặp qua char*
bộ đệm kết thúc null .
struct null_sentinal_t {
template<class Rhs,
std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
>
friend bool operator==(Rhs const& ptr, null_sentinal_t) {
return !*ptr;
}
template<class Rhs,
std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
>
friend bool operator!=(Rhs const& ptr, null_sentinal_t) {
return !(ptr==null_sentinal_t{});
}
template<class Lhs,
std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
>
friend bool operator==(null_sentinal_t, Lhs const& ptr) {
return !*ptr;
}
template<class Lhs,
std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
>
friend bool operator!=(null_sentinal_t, Lhs const& ptr) {
return !(null_sentinal_t{}==ptr);
}
friend bool operator==(null_sentinal_t, null_sentinal_t) {
return true;
}
friend bool operator!=(null_sentinal_t, null_sentinal_t) {
return false;
}
};
ví dụ trực tiếp trong trình biên dịch không có hỗ trợ C ++ 17 đầy đủ; for
vòng lặp mở rộng bằng tay.
begin/end
hoặc một người bạn, tĩnh hoặc miễn phíbegin/end
. Chỉ cần cẩn thận trong không gian tên mà bạn đặt chức năng miễn phí: stackoverflow.com/questions/28242073/iêu