나는 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에 전적으로 의존합니다.