벡터를 이동하면 반복기가 무효화됩니까?
내가 벡터에 반복자가 있다면 a
, 그때는 이동-구조 또는 이동-할당 벡터 b
에서를 a
, 반복자는 여전히 같은 요소를 가리 않습니다 (지금 벡터 b
)? 코드에서 내가 의미하는 바는 다음과 같습니다.
#include <vector>
#include <iostream>
int main(int argc, char *argv[])
{
std::vector<int>::iterator a_iter;
std::vector<int> b;
{
std::vector<int> a{1, 2, 3, 4, 5};
a_iter = a.begin() + 2;
b = std::move(a);
}
std::cout << *a_iter << std::endl; // Is a_iter valid here?
return 0;
}
인가 a_iter
이후 여전히 유효 a
로 이동 된 b
또는 이동에 의해 무효화 반복자는? 참고 std::vector::swap
로 반복자를 무효화하지 않습니다 .
iterator
s가 s 이후에도 여전히 유효 하다고 가정하는 것이 합리적 일 수 있지만 move
표준이 실제로 이것을 보장한다고 생각하지 않습니다. 따라서 반복자는 move
.
나는 표준에서 찾을 수있는 기준이 없다 을 구체적 A가 전에 존재하는 반복자 move
여전히 유효 후 은 move
.
표면적 으로iterator
는 일반적으로 제어 된 시퀀스에 대한 포인터로 구현 된다고 가정하는 것이 합리적으로 보입니다 . 이 경우 반복자는 move
.
그러나의 구현 iterator
은 구현에 따라 정의됩니다. 즉 iterator
, 특정 플랫폼의가 표준에 명시된 요구 사항을 충족하는 한 어떤 방식 으로든 구현할 수 있습니다. 이론적 vector
으로는 인덱스와 함께 클래스로 돌아가는 포인터의 조합으로 구현 될 수 있습니다 . 하면 그건 경우, 그 반복자는 후 무효가 될 것입니다 move
.
iterator
이 방식으로 실제로 구현 되었는지 여부 는 관련이 없습니다. 이러한 방식으로 구현할 수 있으므로 사후 move
반복기가 여전히 유효 하다는 표준의 특정 보증 없이는 유효하다고 가정 할 수 없습니다. 곰 염두에두고이 또한 있다 후 반복자에 대한 이러한 보증 swap
. 이것은 이전 표준에서 명확하게 설명되었습니다. 아마도 a 이후의 반복자에 대해 유사한 설명을하지 않는 것은 단순히 Std위원회의 감독이었을 것입니다. move
그러나 어떤 경우에도 그러한 보장은 없습니다.
따라서 길고 짧은 것은 반복기가 move
.
편집하다:
초안 n3242의 23.2.1 / 11은 다음과 같이 말합니다.
달리 지정되지 않는 한 (명시 적으로 또는 다른 함수와 관련하여 함수를 정의하여) 컨테이너 멤버 함수를 호출하거나 컨테이너를 라이브러리 함수에 대한 인수로 전달해도 해당 컨테이너 내의 개체에 대한 반복자를 무효화하거나 해당 컨테이너 내의 개체 값을 변경하지 않습니다. .
이로 인해 반복기가 유효하다는 결론을 내릴 수 move
있지만 동의하지 않습니다. 예제 코드 a_iter
에서 vector
a
. 이후 move
해당 컨테이너 a
는 확실히 변경되었습니다. 내 결론은 위의 조항이이 경우에 적용되지 않는다는 것입니다.
이동 할당을 이동 구성으로 변경 한 편집이 답을 바꾸는 것 같습니다.
적어도 내가 표 96을 올바르게 읽고 있다면, 이동 구성의 복잡성은 "note B"로 주어지며 std::array
. 그러나 이동 할당 의 복잡성은 선형으로 제공됩니다.
따라서 이동 구성은 본질적으로 소스에서 포인터를 복사하는 것 외에는 선택의 여지가 없으며,이 경우 반복기가 어떻게 무효화 될 수 있는지 확인하기가 어렵습니다.
그러나 이동 할당의 경우 선형 복잡성은 개별 요소를 소스에서 대상으로 이동하도록 선택할 수 있음을 의미하며 ,이 경우 반복기가 거의 확실하게 무효화됩니다.
요소의 이동 할당 가능성은 "의 기존 요소는 모두 이동이 할당되거나 제거됩니다"라는 설명으로 강화됩니다. "파괴 된"부분은 기존 내용을 파괴하고 소스에서 포인터를 "도용"하는 것에 해당하지만 "할당 된 이동"은 대신 소스에서 대상으로 개별 요소를 이동하는 것을 나타냅니다.
이터레이터가 원래 컨테이너에 대한 참조 나 포인터를 유지하는 것을 막을 수있는 것이 없기 때문에 표준에서 명시적인 보증을 찾지 않는 한 유효한 이터레이터에 의존 할 수 없다고 말하고 싶습니다.
tl; dr : 예, a를 이동 std::vector<T, A>
하면 반복기가 무효화 될 수 있습니다.
일반적인 경우 ( std::allocator
제자리에 있음)는 무효화가 발생하지 않지만 보장이없고 컴파일러를 전환하거나 다음 컴파일러 업데이트로 인해 구현이 현재 반복자를 무효화하지 않는다는 사실에 의존하면 코드가 잘못 작동 할 수 있다는 것입니다.
이동 중 할당 :
std::vector
이동 할당 후 반복기가 실제로 유효한 상태를 유지할 수 있는지 여부 는 벡터 템플릿의 할당 자 인식과 연결되며 할당 자 유형 (및 해당 인스턴스)에 따라 다릅니다.
내가 본 모든 구현에서 std::vector<T, std::allocator<T>>
1 의 이동 할당은 실제로 반복기 또는 포인터를 무효화하지 않습니다. 그러나 컨테이너가 할당자를 인식하기 때문에 표준이 일반적으로 인스턴스 의 이동 할당에 대해 반복기가 유효하다는 것을 보장 할 수 없기 때문에 이것을 사용하는 데 문제가 있습니다 std::vector
.
사용자 지정 할당자는 상태를 가질 수 있으며 이동 할당시 전파되지 않고 동일하게 비교되지 않는 경우 벡터는 자체 할당자를 사용하여 이동 된 요소에 대한 저장소를 할당해야합니다.
허락하다:
std::vector<T, A> a{/*...*/};
std::vector<T, A> b;
b = std::move(a);
이제
std::allocator_traits<A>::propagate_on_container_move_assignment::value == false &&
std::allocator_traits<A>::is_always_equal::value == false &&
( 아마도 C ++ 17 기준 )a.get_allocator() != b.get_allocator()
그런 다음 b
새 저장소를 할당하고 요소를 a
하나씩 해당 저장소로 이동 하여 모든 반복기, 포인터 및 참조를 무효화합니다.
The reason is that fulfillment of above condition 1. forbidds move assignment of the allocator on container move. Therefore, we have to deal with two different instances of the allocator. If those two allocator objects now neither always compare equal (2.) nor actually compare equal, then both allocators have a different state. An allocator x
may not be able to deallocate memory of another allocator y
having a different state and therefore a container with allocator x
cannot just steal memory from a container which allocated its memory via y
.
If the allocator propagates on move assignment or if both allocators compare equal, then an implementation will very likely choose to just make b
own a
s data because it can be sure to be able to deallocate the storage properly.
1: std::allocator_traits<std::allocator<T>>::propagate_on_container_move_assignment
and std::allocator_traits<std::allocator<T>>::is_always_equal
both are typdefs for std::true_type
(for any non-specialized std::allocator
).
On move construction:
std::vector<T, A> a{/*...*/};
std::vector<T, A> b(std::move(a));
The move constructor of an allocator aware container will move-construct its allocator instance from the allocator instance of the container which the current expression is moving from. Thus, the proper deallocation capability is ensured and the memory can (and in fact will) be stolen because move construction is (except for std::array
) bound to have constant complexity.
Note: There is still no guarantee for iterators to remain valid even for move construction.
On swap:
Demanding the iterators of two vectors to remain valid after a swap (now just pointing into the respective swapped container) is easy because swapping only has defined behaviour if
std::allocator_traits<A>::propagate_on_container_swap::value == true ||
a.get_allocator() == b.get_allocator()
Thus, if the allocators do not propagate on swap and if they do not compare equal, swapping the containers is undefined behaviour in the first place.
참고URL : https://stackoverflow.com/questions/11021764/does-moving-a-vector-invalidate-iterators
'Development Tip' 카테고리의 다른 글
서브를 실행하면 어떻게됩니까? (0) | 2020.11.15 |
---|---|
Linux에 ASP.NET MVC 배포 : 모범 사례, 도구 및 놀라움 (0) | 2020.11.15 |
C ++ 최적화 프로그램이 clock ()에 대한 호출을 재정렬하는 것이 합법적입니까? (0) | 2020.11.15 |
Objective-C 프로젝트에서 Swift Pod Framework를 가져오고 사용하는 방법 (0) | 2020.11.15 |
C ++ 11에서 메모리를 정렬하는 권장 방법은 무엇입니까? (0) | 2020.11.15 |