Размещение структуры в unordered_map, справочная проблема

У меня есть следующее определение для boost unordered_map

typedef boost::unordered::unordered_map<std::String, CLIENT_STATE> CLIENT_MAP;

Где CLIENT_STATE — это структура, определенная следующим образом:

typedef struct{
unsigned char state;

/* socket fd of the client */
int fd;

/* File path requested by the client */
char file_path [255];

/* Current file offset */
unsigned long int offset;

} CLIENT_STATE;

Когда я пытаюсь добавить элемент в CLIENT_MAP с помощью emplace, CLIENT_MAP создает отдельную копию элемента, не связанную с исходным определением. т. е. любые модификации исходного определения элемента не будут вносить никаких изменений в элемент в unordered_map.

Раньше мне говорили, что использование emplace не будет клонировать мой исходный элемент, он поместит его непосредственно в контейнер, проверьте ссылка

Итак, как лучше всего сделать так, чтобы изменения добавленного элемента в контейнере влияли на исходное определение и наоборот.

Это то, что я имею в виду:

    CLIENT_STATE new_client;
    new_client.offset = 5;
    client_map.emplace("Test", new_client);
    new_client.offset = 10;

    cout << client_map["Test"].offest;

cout не напечатает 10, он напечатает 5.

В настоящее время, чтобы решить эту проблему, после элемента unordered_map я работаю с возвращенным std::pair и делаю модификацию, но я думаю, что это неэффективный способ сделать это.


person IoT    schedule 24.04.2014    source источник
comment
emplace использует свои аргументы в качестве аргументов ctor - в случае unordered_map ctor, который вызывается emplace, - это ctor ::std::pair<key_type, value_type>   -  person gha.st    schedule 24.04.2014
comment
Итак, что я должен сделать, чтобы получить то, что я хочу?   -  person IoT    schedule 24.04.2014


Ответы (2)


emplace позволяет создавать объект из переданных в него аргументов без необходимости создавать объект, который будет передан в первую очередь, экономя накладные расходы за счет удаления конструкции копирования, которая обычно возникает в результате создания объектов, которые должны быть вставлены.

Вы можете достичь того, чего хотите, используя указатели

typedef boost::unordered::unordered_map<std::String, CLIENT_STATE* > CLIENT_MAP;

Но использование указателей может оказаться проблематичным для обработки памяти, например, для удаления объектов и т. д.

Вы можете использовать boost::shared_ptr что-то вроде следующего: (я не эксперт по shared_ptr/boost)


include <boost/shared_ptr.hpp>

boost::unordered::unordered_map<std::String, 
                  boost::shared_ptr<CLIENT_STATE> > client_map ;

std::string s ="client1" ;
CLIENT_STATE *c1 = new CLIENT_STATE;
//c1->state, c1->id, etc
my_map[t] = c1 ;

Если вам не нужно делать копии объектов, хранящихся на карте, вы можете использовать std::unique_ptr

person P0W    schedule 24.04.2014
comment
А если я хочу найти значение с помощью ключа, возвращаемый итератор будет указателем на указатель? Так как каждый объект у меня уникален. Лучше использовать unique_ptr правильно? - person IoT; 24.04.2014
comment
@HA-AS да, итерация по client_map, где shared_ptr в качестве ключа является указателем на указатель, первый указатель - это сам итератор, который указывает на значение на карте - person P0W; 24.04.2014
comment
Я получаю ошибки при компиляции файла, пожалуйста, проверьте мое редактирование - person IoT; 24.04.2014
comment
Не могли бы вы помочь мне, в чем проблема? Извините, я не знаю, как правильно отредактировать эту ошибку - person IoT; 24.04.2014
comment
@HA-AS Пожалуйста, не редактируйте ответ с длинным сообщением об ошибке - просто сообщите автору местонахождение ошибки и первое сообщение об ошибке от компилятора. Я уверен, что он рассмотрит это. - person xxbbcc; 24.04.2014
comment
@ HA-AS Я только что рассказал вам об одном из способов добиться этого, и я предлагаю вам сначала изучить / прочитать больше о shared_ptr вместо того, чтобы слепо копировать непроверенный код. Также не добавляйте ошибки ни в какие ответы на SO, очень вероятно, что они будут отклонены. - person P0W; 24.04.2014
comment
@xxbbcc ошибка в моем коде с использованием его метода. Вот почему я отредактировал ответ - person IoT; 24.04.2014

Вы не хотите использовать emplace. То, что вы хотите, называется семантикой общего указателя. Однако для будущих посетителей этого вопроса может быть полезно объяснить, как правильно использовать emplace с ассоциативным контейнером.

Как правильно заметил @gha.st, emplace вызовет конструктор std::pair<std::string, CLIENT_STATE>, который создаст копии объектов string и CLIENT_STATE.

Начиная с C++11, std::pair имеет дополнительную перегруженную конструкцию конструктора, принимающую первый аргумент типа тега std::piecewise_construct_t, который позволяет самим элементам std::pair создаваться на месте. Наблюдать:

client_map.emplace(std::piecewise_construct,
                   std::forward_as_tuple("Test"),
                   std::forward_as_tuple(5));

Теперь emplace вызовет перегруженный тегом конструктор pair, который, в свою очередь, создаст два своих члена данных из аргументов в переданных кортежах.

В этом случае CLIENT_STATE является агрегатным типом, поэтому у него нет конструктора, который мы можем вызвать с помощью int, подобного этому. Но вышеописанный метод является общим для использования с типичными типами классов C++. Лучшее, что мы можем сделать для агрегата, — это переместить его из временного объекта:

client_map.emplace("Test", CLIENT_STATE{ 0, 0, "", 5 });

В данном случае это бесполезно, потому что члены CLIENT_STATE не могут быть перестроены, поэтому перемещение — это то же самое, что и копия.

person Oktalist    schedule 24.04.2014