La mia domanda è quella di sicurezza. Ho cercato cplusplus.com e cppreference.com e sembra che manchino della sicurezza dell'iteratore durante std :: move. In particolare: è sicuro chiamare std :: unordered_map :: erase (iterator) con un iteratore il cui oggetto è stato spostato? codice di esempio: (?)Spostare gli oggetti da un file non ordinato a un altro contenitore
#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;
}
Il motivo che mi chiedo è che mi colpisce il fatto che spostando la coppia dal iteratore del unordered_map può pertanto * re * spostare valore della chiave memorizzata sulla iteratore e quindi invalidare il suo hash; pertanto eventuali operazioni su di esso in seguito potrebbero comportare un comportamento indefinito. A meno che non sia così?
Penso che valga la pena notare che quanto sopra sembra funzionare correttamente come previsto in GCC 4.8.2, quindi sto cercando di vedere se mi manca la documentazione che supporta o che non supporta esplicitamente il comportamento.
E così è in realtà chiama un ctor copia per std :: coppia? Quindi diciamo che il secondo parametro template è una struttura complessa ... Non otterrei quindi nulla da std :: move() in questa situazione? Dovrei quindi spostare la struttura complessa in una nuova coppia? –
inetknght
@inetknght: Questo è quello che pensavo all'inizio. Tuttavia, sto facendo test ora che sembrano indicare che non è il caso. Quando creo 'std :: pair' e uso 'std :: move' su di esso, sembra che la seconda stringa sia di fatto vuota, anche se la prima rimane intatta come previsto. Dovrò indagare ulteriormente su questo. –
Dopo aver letto le modifiche - quindi il primo oggetto viene copiato ma il secondo viene spostato. Hmmm ... quindi se il primo oggetto fosse una struttura complessa, potrebbe essere terribile. Nell'utilizzare un shared_ptr, mi chiedo la spesa del mutex in questo, ma questo è al di fuori della portata di questa domanda e se c'è un modo per aggirare questo. Grazie per l'aiuto! – inetknght