vector<int> v;
v.push_back(1);
v.push_back(v[0]);
두 번째 push_back으로 인해 재 할당이 발생하면 벡터의 첫 번째 정수에 대한 참조가 더 이상 유효하지 않습니다. 안전하지 않습니까?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
안전한가요?
vector<int> v;
v.push_back(1);
v.push_back(v[0]);
두 번째 push_back으로 인해 재 할당이 발생하면 벡터의 첫 번째 정수에 대한 참조가 더 이상 유효하지 않습니다. 안전하지 않습니까?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
안전한가요?
답변:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 은이 문제 (또는 그와 비슷한 것)를 표준의 잠재적 결함으로 처리 한 것처럼 보입니다 .
1) const 참조로 얻은 매개 변수는 함수 실행 중에 변경 될 수 있습니다
예 :
주어진 std :: vector v :
v. 삽입 (v.begin (), v [2]);
v [2]는 벡터의 요소를 움직여 변경할 수 있습니다
제안 된 해결책은 이것이 결함이 아니라는 것입니다.
표준은 작동하지 않을 권한을 부여하지 않으므로 vector :: insert (iter, value)가 작동해야합니다.
v.insert(v.begin(), v[2]);
재 할당을 트리거 할 수 없습니다. 그렇다면 이것이 어떻게 질문에 대답합니까?
예, 안전하며 표준 라이브러리 구현은 후프를 뛰어 넘습니다.
구현 자가이 요구 사항을 어떻게 든 23.2 / 11로 추적한다고 생각하지만 방법을 알 수 없으며 더 구체적인 것을 찾을 수 없습니다. 내가 찾을 수있는 가장 좋은 것은이 기사입니다.
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
libc ++와 libstdc ++의 구현을 검사하면 그것들도 안전하다는 것을 알 수 있습니다.
vec.insert(vec.end(), vec.begin(), vec.end());
있습니까?
vector.push_back
그렇지 않으면 지정합니다. "새로운 크기가 이전 용량보다 크면 재 할당이 발생합니다." 그리고 (at reserve
) "재 할당은 시퀀스의 요소를 참조하는 모든 참조, 포인터 및 반복자를 무효화합니다."
이 표준은 첫 번째 예조차도 안전하다는 것을 보장합니다. 인용 C ++ 11
[sequence.reqmts]
3 표 100 및 101에서 ...
X
는 시퀀스 컨테이너 클래스, 유형의 요소를 포함a
하는 값 , ... 는 lvalue 또는 const rvalue를 나타냅니다.X
T
t
X::value_type
16 표 101 ...
표현
a.push_back(t)
반환 유형void
운영 의미론 의 사본에 추가t.
요구를 :T
한다CopyInsertable
로X
. 컨테이너basic_string
,deque
,list
,vector
따라서 정확히 사소한 것은 아니지만 구현시를 수행 할 때 참조가 무효화되지 않도록해야합니다 push_back
.
t
. 유일한 질문은 사본을 만들기 전후에 있습니다. 마지막 문장이 확실하지 않습니다.
t
나열된 사전 조건을 충족 하므로 설명 된 동작이 보장됩니다. 구현에서는 전제 조건을 무효화 한 다음 지정된대로 동작하지 않도록 변명으로 사용할 수 없습니다.
for_each
가 반복자를 무효화하지 않아야 한다고 생각 합니다. 에 대한 참조를 얻을 수 for_each
는 없지만 "op 및 binary_op는 반복자 또는 하위 범위를 무효화하지 않습니다"와 같은 일부 알고리즘 텍스트에서 볼 수 있습니다.
가장 간단한 구현은 push_back
필요한 경우 먼저 벡터를 재 할당하고 참조를 복사하는 것이므로 첫 번째 예제가 안전하다는 것은 분명하지 않습니다 .
그러나 적어도 Visual Studio 2010에서는 안전합니다. 구현은 push_back
벡터의 요소를 뒤로 밀 때 사례를 특수하게 처리합니다. 코드는 다음과 같이 구성됩니다.
void push_back(const _Ty& _Val)
{ // insert element at end
if (_Inside(_STD addressof(_Val)))
{ // push back an element
...
}
else
{ // push back a non-element
...
}
}
이것은 표준을 보장하지는 않지만 다른 데이터 포인트로서 LLVM의 libc ++에v.push_back(v[0])
안전합니다 .
std::vector::push_back
__push_back_slow_path
메모리를 재 할당해야 할 때 libc ++의 호출 :
void __push_back_slow_path(_Up& __x) {
allocator_type& __a = this->__alloc();
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1),
size(),
__a);
// Note that we construct a copy of __x before deallocating
// the existing storage or moving existing elements.
__alloc_traits::construct(__a,
_VSTD::__to_raw_pointer(__v.__end_),
_VSTD::forward<_Up>(__x));
__v.__end_++;
// Moving existing elements happens here:
__swap_out_circular_buffer(__v);
// When __v goes out of scope, __x will be invalid.
}
__swap_out_circular_buffer
이 구현은 실제로 안전합니다.
__swap_out_circular_buffer
. (나는 그것을 언급하기 위해 몇 가지 의견을 추가했습니다.)
첫 번째 버전은 확실히 안전하지 않습니다 :
표준 라이브러리 컨테이너 또는 문자열 멤버 함수를 호출하여 얻은 반복자 작업은 기본 컨테이너에 액세스 할 수 있지만 수정해서는 안됩니다. [참고 : 특히 반복자를 무효화하는 컨테이너 작업은 해당 컨테이너와 연결된 반복자 작업과 충돌합니다. — 끝 참고]
섹션 17.6.5.9에서
이것은 사람들이 일반적으로 스레딩과 관련하여 생각하는 데이터 레이스에 관한 섹션입니다 ...하지만 실제 정의에는 "이전의 일"관계가 포함 push_back
되어 있습니다. 여기서, 참조 무효화는 새로운 테일 요소를 복사 구성하는 것과 관련하여 순서대로 정의되지 않은 것으로 보인다.
v[0]
반복자 의 결과는 마찬가지로 반복자 push_back()
가 아닙니다. 따라서 언어 변호사의 관점에서 볼 때 귀하의 주장은 무효입니다. 죄송합니다. 나는 대부분의 반복자가 포인터이며 반복자를 무효화하는 점은 참조의 경우와 거의 동일하지만 인용 한 표준의 일부는 현재 상황과 관련이 없다는 것을 알고 있습니다.
x.push_back(x[0])
는 SAFE 라고 말합니다 .
push_back은 참조가 아닌 값을 복사하므로 모두 안전합니다. 포인터를 저장하는 경우 벡터에 관한 한 여전히 안전하지만 벡터의 두 요소가 동일한 데이터를 가리키고 있음을 알고 있습니다.
섹션 23.2.1 일반 컨테이너 요구 사항
16
- a.push_back (t) t의 사본을 추가합니다. 요구 사항 : T는 X에 CopyInsertable이어야합니다.
- a.push_back (rv) rv의 사본을 추가합니다. 요구 사항 : T는 X로 MoveInsertable이어야합니다.
따라서 push_back을 구현 하면 사본 v[0]
이 삽입 되었는지 확인해야합니다 . 예를 들어, 복사하기 전에 재 할당하는 구현을 가정 할 때 v[0]
, 사양 의 사본을 추가하거나 위반하는 것은 확실하지 않습니다 .
push_back
그러나 벡터의 크기 도 조정 하고 순진한 구현에서는 복사가 발생하기 전에 참조를 무효화 합니다. 따라서 표준의 인용으로 이것을 뒷받침 할 수 없다면 나는 그것을 잘못 생각할 것입니다.
push_back
값을 벡터에 복사합니다. 그러나 (내가 볼 수있는 한) 재 할당 후에 발생할 수 있습니다.이 시점에서 복사하려는 참조는 더 이상 유효하지 않습니다.
23.3.6.5/1부터 : Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
우리가 마지막에 삽입하고 있기 때문에, 더 참조는 무효화되지 않습니다 경우 벡터의 크기가 변경되지 않습니다. 따라서 벡터 capacity() > size()
가 작동하면 작동이 보장되고 그렇지 않으면 정의되지 않은 동작이 보장됩니다.
references
인용 부분에 관심이 있습니다.
push_back
).
push_back
. 또 다른 포스터 에는 버그가 언급되어있어 귀하가 설명하는 사례를 제대로 처리하지 못했습니다. 내가 알 수있는 한, 아무도 이것이 버그가 아니라고 주장하지 않았습니다. 결론을 내릴 수는 없습니다.