나는 unorder_map :: insert ()가 삽입 한 변수를 파괴하는 매우 이상한 버그를 추적하는 내 인생의 3 일을 잃었습니다. 이 매우 명확하지 않은 동작은 최신 컴파일러에서만 발생합니다. clang 3.2-3.4 및 GCC 4.8 이이 "기능"을 보여주는 유일한 컴파일러 라는 것을 발견했습니다 .
다음은 문제를 보여주는 주요 코드베이스의 일부 축소 된 코드입니다.
#include <memory>
#include <unordered_map>
#include <iostream>
int main(void)
{
std::unordered_map<int, std::shared_ptr<int>> map;
auto a(std::make_pair(5, std::make_shared<int>(5)));
std::cout << "a.second is " << a.second.get() << std::endl;
map.insert(a); // Note we are NOT doing insert(std::move(a))
std::cout << "a.second is now " << a.second.get() << std::endl;
return 0;
}
나는 아마도 대부분의 C ++ 프로그래머와 마찬가지로 다음과 같은 출력을 기대할 것입니다.
a.second is 0x8c14048
a.second is now 0x8c14048
그러나 clang 3.2-3.4 및 GCC 4.8을 사용하면 대신 다음과 같이 표시됩니다.
a.second is 0xe03088
a.second is now 0
http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/ 에서 unordered_map :: insert ()에 대한 문서를 자세히 살펴보기 전까지는 말이되지 않을 수 있습니다 . 여기서 오버로드 2 번은 다음과 같습니다.
template <class P> pair<iterator,bool> insert ( P&& val );
탐욕스러운 범용 참조 이동 오버로드이며 다른 오버로드와 일치하지 않는 것을 소비하고 이를 value_type 으로 구성 합니다. 그렇다면 위의 코드가 아마도 대부분의 예상대로 unorder_map :: value_type 오버로드가 아닌이 오버로드를 선택한 이유는 무엇입니까?
대답은 얼굴에서 당신을 쳐다보고 : unordered_map도 :: VALUE_TYPE는 한 쌍의 < CONST INT, 표준 : : shared_ptr의>와 컴파일러가 제대로 그 한 쌍의 <생각 INT , 표준 : : shared_ptr의> 변환 될 수 없습니다. 따라서 컴파일러는 이동 범용 참조 오버로드를 선택 하고 프로그래머가 std :: move ()를 사용하지 않음 에도 불구하고 변수가 파괴되는 것이 괜찮다는 것을 나타내는 일반적인 규칙 임에도 불구하고 원본 을 파괴합니다. 따라서 동작을 파괴 삽입은 사실상 올바른 은 C ++ 11 표준에 따라, 세 컴파일러이었다 잘못된 .
이 버그를 진단하는 데 3 일이 걸린 이유를 이제 알 수 있습니다. unorder_map에 삽입되는 유형이 소스 코드 용어로 멀리 정의 된 typedef 인 대규모 코드베이스에서는 전혀 명확하지 않았으며 typedef가 value_type과 동일한 지 확인하는 일이 누구에게도 발생하지 않았습니다.
그래서 Stack Overflow에 대한 내 질문 :
왜 오래된 컴파일러는 새로운 컴파일러처럼 삽입 된 변수를 파괴하지 않습니까? 내 말은, GCC 4.7조차도 이것을 수행하지 않으며 꽤 표준을 준수합니다.
컴파일러를 업그레이드하면 작동하던 코드가 갑자기 작동을 멈추기 때문에이 문제가 널리 알려져 있습니까?
C ++ 표준위원회가이 동작을 의도 했습니까?
더 나은 동작을 제공하기 위해 unorder_map :: insert ()를 수정하는 방법을 제안 하시겠습니까? 여기에 지원이 있으면이 행동을 WG21에 N 노트로 제출하고 더 나은 행동을 구현하도록 요청하기 때문에 이것을 묻습니다.
4.9.0 20131223 (experimental)각각 gcc 4.8.2를 사용하고 있습니다. 출력은 a.second is now 0x2074088 나를 위해 (또는 유사합니다).
a. 복사본을 만들어야합니다. 또한이 동작은 컴파일러가 아닌 stdlib에 전적으로 의존합니다.