2014-06-09 27 views
12

Sorum şu: Güvenlik. Cplusplus.com ve cppreference.com'u aradım ve std :: move sırasında yineleyici güvenliğinden yoksun görünüyorlar. Özellikle: std :: unordered_map :: delete (yineleyici) nesnesi taşınmış bir yineleyici ile çağırmak güvenli midir? Örnek kod: (?)Nesneleri bir unordered_map'ten başka bir kaba taşıma

#include <unordered_map> 
#include <string> 
#include <vector> 
#include <iostream> 
#include <memory> 

class A { 
public: 
    A() : name("default ctored"), value(-1) {} 
    A(const std::string& name, int value) : name(name), value(value) { } 
    std::string name; 
    int value; 
}; 
typedef std::shared_ptr<const A> ConstAPtr; 

int main(int argc, char **argv) { 
    // containers keyed by shared_ptr are keyed by the raw pointer address 
    std::unordered_map<ConstAPtr, int> valued_objects; 

    for (int i = 0; i < 10; ++i) { 
     // creates 5 objects named "name 0", and 5 named "name 1" 
     std::string name("name "); 
     name += std::to_string(i % 2); 

     valued_objects[std::make_shared<A>(std::move(name), i)] = i * 5; 
    } 

    // Later somewhere else we need to transform the map to be keyed differently 
    // while retaining the values for each object 

    typedef std::pair<ConstAPtr, int> ObjValue; 

    std::unordered_map<std::string, std::vector<ObjValue> > named_objects; 

    std::cout << "moving..." << std::endl; 

    // No increment since we're using .erase() and don't want to skip objects. 
    for (auto it = valued_objects.begin(); it != valued_objects.end();) { 
     std::cout << it->first->name << "\t" << it->first.value << "\t" << it->second << std::endl; 

     // Get named_vec. 
     std::vector<ObjValue>& v = named_objects[it->first->name]; 
     // move object :: IS THIS SAFE?? 
     v.push_back(std::move(*it)); 

     // And then... is this also safe??? 
     it = valued_objects.erase(it); 
    } 

    std::cout << "checking... " << named_objects.size() << std::endl; 
    for (auto it = named_objects.begin(); it != named_objects.end(); ++it) { 
     std::cout << it->first << " (" << it->second.size() << ")" << std::endl; 
     for (auto pair : it->second) { 
      std::cout << "\t" << pair.first->name << "\t" << pair.first->value << "\t" << pair.second << std::endl; 
     } 
    } 

    std::cout << "double check... " << valued_objects.size() << std::endl; 
    for (auto it : valued_objects) { 
     std::cout << it.first->name << " (" << it.second << ")" << std::endl; 
    } 

    return 0; 
} 

Bunu sormamın sebebi bu beni vurur olduğunu unordered_map en İlerleticiden çiftini hareketli may nedenle * re * yineleyici saklanan anahtarı değerini taşımak ve bu nedenle onun karmasını geçersiz; Bu nedenle, sonrasında yapılan herhangi bir işlem, tanımlanmamış davranışlarla sonuçlanabilir. Tabii öyle değil mi?

Yukarıdakilerin, GCC 4.8.2'de belirtildiği gibi başarılı bir şekilde çalıştığı görülüyor. Bu yüzden, desteklemeyi destekleyen veya açık bir şekilde davranışı desteklemeyen belgeleri kaçırıp kaçırmadığımı görmek istiyorum.

cevap

5
// move object :: IS THIS SAFE?? 
v.push_back(std::move(*it)); 

Evet, bu güvenlidir, çünkü bu aslında anahtarı değiştirmez. Yapamaz çünkü anahtar const. *it'un türü std::pair<const ConstAPtr, int>'dur. Taşınırken, ilk üye (const ConstAPtr) aslında taşınmaz. std::move tarafından bir r değerine dönüştürülür ve const ConstAPtr&& olur. Ancak bu, const olmayan bir ConstAPtr&& bekler hareket ediciyle eşleşmiyor. Böylece kopya kurucusu yerine çağrılır.

+0

Ve bu yüzden aslında std :: pair için bir kopya ctor çağırır? Öyleyse ikinci şablon parametresinin karmaşık bir yapı olduğunu söyleyelim ... Bu durumda std :: move() öğesinden hiçbir şey elde edemem? Bu yüzden karmaşık yapıyı yeni bir parka taşımam gerekir mi? – inetknght

+0

@inetknght: İlk başta düşündüğüm şey buydu. Ancak, şimdi durum böyle olmadığını gösteren testler yapıyorum. Bir std :: çifti 'oluşturduğumda ve' std :: move 'sözcüğünü kullandığımda, ikinci dizenin aslında boş olduğu anlaşılır, beklenen. Bunu daha fazla araştırmam gerekecek. –

+0

Düzenlemelerinizi okuduktan sonra - ilk nesne kopyalanır ancak ikincisi taşınır. Hmmm ... eğer ilk nesne karmaşık bir yapı olsaydı, bu korkunç olabilirdi. Bir shared_ptr kullanarak, muteks'in bunun için harcadığı parayı merak ediyorum ama bu sorunun kapsamı dışında ve bunun bir yolu var mı? Yardım ettiğin için teşekkür ederim! – inetknght

İlgili konular