Unordered_map :: insert () eklediğiniz değişkeni yok ettiği çok garip bir hatayı izleyerek hayatımın üç gününü kaybettim. Bu oldukça açık olmayan davranış yalnızca çok yeni derleyicilerde görülür: clang 3.2-3.4 ve GCC 4.8'in bu "özelliği" gösteren tek derleyiciler olduğunu buldum .
İşte ana kod tabanımdan sorunu gösteren bazı azaltılmış kodlar:
#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;
}
Muhtemelen çoğu C ++ programcısı gibi, çıktının şöyle görünmesini beklerdim:
a.second is 0x8c14048
a.second is now 0x8c14048
Ancak clang 3.2-3.4 ve GCC 4.8 ile bunun yerine şunu alıyorum:
a.second is 0xe03088
a.second is now 0
Http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/ adresinde unordered_map :: insert () belgelerini yakından inceleyene kadar bu hiç mantıklı gelmeyebilir, burada 2 numaralı aşırı yük:
template <class P> pair<iterator,bool> insert ( P&& val );
Hangi açgözlü bir evrensel referans hareket aşırı yük olan bir şey diğer aşırı herhangi eşleşen değil, tüketen inşa taşımak bir VALUE_TYPE içine. Öyleyse neden yukarıdaki kodumuz bu aşırı yüklemeyi seçti ve muhtemelen çoğu kişinin beklediği gibi unordered_map :: value_type aşırı yüklemesini seçmedi?
Cevap yüzünüze bakıyor: unordered_map :: value_type bir çifttir < const int, std :: shared_ptr> ve derleyici doğru bir şekilde < int , std :: shared_ptr> çiftinin dönüştürülebilir olmadığını düşünecektir . Dolayısıyla derleyici hareket evrensel referans aşırı seçer ve o, orijinali yok eder rağmen bir değişken tahrip alma ile Tamam göstermek için tipik kongre olan std :: hamle () kullanmayan programcı. Bu nedenle, eklenti yok etme davranışı aslında C ++ 11 standardına göre doğrudur ve daha eski derleyiciler yanlıştır .
Bu hatayı teşhis etmek için neden üç günümü aldığımı muhtemelen şimdi anlayabilirsiniz. Unordered_map'e eklenen türün, kaynak kodu terimlerinde çok uzakta tanımlanmış bir typedef olduğu büyük bir kod tabanında hiç de açık değildi ve typedef'in değer_type ile aynı olup olmadığını kontrol etmek hiç kimsenin aklına gelmedi.
Yani Stack Overflow'a sorularım:
Eski derleyiciler neden daha yeni derleyiciler gibi eklenen değişkenleri yok etmez? Demek istediğim, GCC 4.7 bile bunu yapmıyor ve oldukça standartlara uyuyor.
Bu sorun yaygın olarak biliniyor mu, çünkü kesinlikle derleyicileri yükseltmek, eskiden çalışan kodun aniden çalışmayı durdurmasına neden olacak mı?
C ++ standartları komitesi bu davranışı amaçladı mı?
Daha iyi davranış sağlamak için unordered_map :: insert () değiştirilmesini nasıl önerirsiniz? Bunu soruyorum çünkü burada destek varsa, bu davranışı WG21'e bir N notu olarak göndermeyi ve onlardan daha iyi bir davranış uygulamasını istemeyi düşünüyorum.
4.9.0 20131223 (experimental)
sırasıyla gcc 4.8.2 kullanıyorum . Çıktı benim için a.second is now 0x2074088
(veya benzeri).
a
değildir. Bir kopya yapmalı. Ayrıca, bu davranış derleyiciye değil tamamen stdlib'e bağlıdır.