왜 표준 end()
이 실제 끝이 아닌 마지막 끝을 정의 합니까?
왜 표준 end()
이 실제 끝이 아닌 마지막 끝을 정의 합니까?
답변:
가장 좋은 주장은 Dijkstra 자신이 만든 주장입니다 .
범위의 크기가 단순한 차이가 되길 원합니다. end - begin ;
하한을 포함하는 것은 서열이 빈 것으로 변질 될 때 더욱 "자연적"이며, 또한 하한을 제외한 대안 은 "시작 전"센티넬 값의 존재를 필요로하기 때문이다.
왜 1이 아닌 0으로 계산을 시작하는지 이유를 정당화해야하지만 그것은 귀하의 질문의 일부가 아닙니다.
[시작, 끝] 규칙 뒤에 숨은 지혜는 자연스럽게 연결되는 범위 기반 구성에 대한 여러 중첩 또는 반복 호출을 처리하는 알고리즘이있을 때마다 시간을 낭비합니다. 반대로, 이중 폐쇄 범위를 사용하면 개별적으로 발생하고 매우 불쾌하고 시끄러운 코드가 발생할 수 있습니다. 예를 들어, 파티션 [ n 0 , n 1 ) [ n 1 , n 2 ) [ n 2 , n 3 )을 고려하십시오. 또 다른 예로는 표준 반복 루프 for (it = begin; it != end; ++it)
가 있는데, 이는 end - begin
시간 을 실행 합니다. 양쪽 끝이 모두 포함 된 경우 해당 코드는 읽기가 쉽지 않으며 빈 범위를 처리하는 방법을 상상해보십시오.
마지막으로, 우리는 왜 카운트가 0에서 시작해야하는지에 대한 좋은 논쟁을 할 수 있습니다. 방금 설정 한 범위에 대한 반 개방 규칙을 통해 N 개의 요소 범위 (배열의 멤버를 열거하는) 가 주어진 다면 0은 자연스러운 "시작"이므로 어색한 오프셋이나 수정없이 범위를 [0, N ) 으로 쓸 수 있습니다 .
요컨대 1
, 범위 기반 알고리즘의 모든 곳에서 숫자를 볼 수 없다는 사실 은 [시작, 끝] 규칙의 직접적인 결과이며 동기 부여입니다.
begin
와 end
같은 int
값의 0
및 N
각각, 완벽하게 맞습니다. 논란의 여지가 있지만, 그것은 !=
전통적인 것보다 더 자연스러운 조건 <
이지만, 더 일반적인 컬렉션에 대해 생각하기 시작할 때까지는 결코 발견하지 못했습니다.
++
-incrementable iterator template step_by<3>
을 작성해야합니다.
!=
자신이 사용해야하는 경우 <
, 다음 그 것이다 버그. 그건 그렇고, 그 오류의 왕은 단위 테스트 또는 어설 션으로 쉽게 찾을 수 있습니다.
당신이 반복자가 가리키는하지 고려한다면 사실, 반복자 관련 물건을 많이 갑자기 훨씬 더 의미 에서 시퀀스의 요소 만 사이에 그것을 다음 요소를 오른쪽으로 접근 역 참조와 함께. 그런 다음 "한 과거 끝"반복자가 갑자기 즉시 의미가 있습니다.
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
^ ^
| |
begin end
분명히 begin
시퀀스의 시작을 end
가리키고 동일한 시퀀스의 끝을 가리 킵니다. 역 참조는 begin
요소를 액세스 A
하고, 역 참조는 end
그것에 어떤 요소 권리가 없기 때문에 이해되지 않는다. 또한 i
중간에 반복자 를 추가 하면
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
^ ^ ^
| | |
begin i end
당신은 즉시에서 요소의 범위 볼 begin
에이 i
요소를 포함 A
하고 B
의 요소의 범위 반면 i
에이 end
요소가 들어 C
와 D
. 역 참조 i
는 오른쪽 요소, 즉 두 번째 시퀀스의 첫 번째 요소를 제공합니다.
리버스 이터레이터에 대한 "off-by-one"조차도 갑자기 다음과 같이 분명해집니다.
+---+---+---+---+
| D | C | B | A |
+---+---+---+---+
^ ^ ^
| | |
rbegin ri rend
(end) (i) (begin)
아래에 괄호 안에 해당 비역 (기본) 반복자를 작성했습니다. 당신은 참조 역 반복자에 속하는 i
(I의 이름 적이있는 ri
) 여전히 요소들 사이에서 점 B
과 C
. 그러나 순서를 반전 시키면 요소 B
가 오른쪽에 있습니다.
foo[i]
)이 위치 바로 뒤에 있는 항목의 약어 인 경우 더 잘 설명 될 수 있다고 생각하지만 이것은 IMHO의 가장 좋은 대답 i
입니다. 그것에 대해 생각하면, 많은 알고리즘이 인접한 항목 쌍과 함께 작동하고 " 위치 i의 어느 한쪽에있는 항목은 "i 및 i + 1에있는 항목"보다 깨끗할 수 있습니다.
begin[0]
(임의 액세스 반복자를 가정) 내 예제 시퀀스에 1
요소가 없으므로 element 0
에 액세스 합니다.
start()
: 특정 프로세스를 시작하기 위해 클래스에서 함수를 정의해야하는 경우 또는 기존 프로세스와 충돌하면 성가 시게됩니다).
그때부터
size() == end() - begin() // For iterators for whom subtraction is valid
그리고 당신은 같은 어색한 일 을 할 필요가 없습니다
// Never mind that this is INVALID for input iterators...
bool empty() { return begin() == end() + 1; }
당신은 실수로 쓰지 않습니다 잘못된 코드 등이
bool empty() { return begin() == end() - 1; } // a typo from the first version
// of this post
// (see, it really is confusing)
bool empty() { return end() - begin() == -1; } // Signed/unsigned mismatch
// Plus the fact that subtracting is also invalid for many iterators
또한 : 유효한 요소를 가리키는 경우 무엇이 find()
반환 end()
됩니까? 잘못된 반복자를 반환하는 다른 멤버
를 정말로 원 하십니까 ?!
두 개의 이터레이터는 이미 충분히 고통 스럽습니다 ...invalid()
아, 이 관련 게시물을 참조하십시오 .
는 경우 end
마지막 요소 전과 어떻게 것 insert()
진정한 끝!
반 폐쇄 범위의 반복자 관용구 [begin(), end())
는 원래 일반 배열에 대한 포인터 산술을 기반으로합니다. 해당 작동 모드에서는 배열과 크기가 전달 된 함수가 있습니다.
void func(int* array, size_t size)
[begin, end)
해당 정보가있을 때 반 폐쇄 범위로 변환하는 것은 매우 간단합니다.
int* begin;
int* end = array + size;
for (int* it = begin; it < end; ++it) { ... }
완전히 닫힌 범위로 작업하기가 더 어렵습니다.
int* begin;
int* end = array + size - 1;
for (int* it = begin; it <= end; ++it) { ... }
배열에 대한 포인터는 C ++의 반복자이며 구문은이를 허용하도록 설계되었으므로 호출하는 std::find(array, array + size, some_value)
것보다 호출 하는 것이 훨씬 쉽습니다 std::find(array, array + size - 1, some_value)
.
당신이 반 폐쇄 범위에서 작동하는 경우 또한, 당신은 사용할 수 있습니다 !=
(귀하의 사업자가 올바르게 정의 된 경우) becuase, 최종 상태를 확인하기 위해 연산자를 <
의미한다 !=
.
for (int* it = begin; it != end; ++ it) { ... }
그러나 완전히 닫힌 범위 에서이 작업을 수행하는 쉬운 방법은 없습니다. 당신은 붙어 있습니다 <=
.
C ++에서 지원 <
하고 >
조작 하는 유일한 반복자 는 랜덤 액세스 반복자입니다. <=
C ++에서 모든 반복자 클래스에 대해 연산자 를 작성해야한다면 모든 반복자를 완전히 비교할 수 있어야하고 덜 반복적 인 반복자 (예 : 양방향 반복자 std::list
또는 입력 반복자) 를 작성하기위한 선택 사항이 적습니다. iostreams
C ++에서 완전히 닫힌 범위를 사용하는 경우) 에서 작동 합니다.
으로 end()
끝을지나 가리키는 하나, 그것은 for 루프와 컬렉션 반복 간단합니다 :
for (iterator it = collection.begin(); it != collection.end(); it++)
{
DoStuff(*it);
}
함께 end()
마지막 요소를 가리키는 루프는 복잡 할 것입니다 :
iterator it = collection.begin();
while (!collection.empty())
{
DoStuff(*it);
if (it == collection.end())
break;
it++;
}