std::map с ключом std::string*

Я использую std::map с const std::string ключами, и я подумал, что было бы неплохо не перемещать ключи по всему стеку, поэтому я изменил тип ключа на указатель:

class less_on_star : less<const string*> {
  public:
    virtual bool operator() (const string* left, const string *right);
};

less_on_star::operator() (const string* left, const string *right) {
  return *left < *right;
}

class Foo {
  private:
    map<const string*, Bar*, less_on_star> bars;
}

Некоторое время он работал нормально, затем я начал получать ошибки сегментации, из-за которых строковый ключ терял свою натуру. Поле _M_p указывало на NULL или 0x2, но ключи всегда были целы, когда я вставлял в карту:

bars[new string(on_stack_string)] = bar;

В gdb это выглядело так, как будто new string(on_stack_string) указывало поле _M_p на обычное расположение кучи, а не на значение стека. Есть ли что-то особенное в std::string, что его нельзя использовать в подобных структурах данных? Может быть, я еще что-то тупил с ключами, но я не могу представить, что бы это могло быть.


person Byron Hawkins    schedule 09.03.2014    source источник
comment
std::string внутренне использует выделенную кучу памяти для символьных данных. В g++ sizeof(std::string) равно 24. Поэтому, когда вы думали, что было бы неплохо не перемещать ключи по всему стеку, вы наверняка думали о чем-то другом. Что касается этой ошибки, она, вероятно, как-то связана с копированием g++ при записи. Бьюсь об заклад, строка без указателя покидает область действия и удаляет данные кучи, поскольку строка указателя используется только как временная и никогда не записывается.   -  person KitsuneYMG    schedule 09.03.2014
comment
Как ты успеваешь за струнами? Похоже, вы неправильно пересчитываете строки. Возможно, вместо этого использовал shared_ptr‹. Лично я предпочел бы хранить саму строку в качестве ключа. Единственный минус, о котором я могу думать, это то, что вам, возможно, придется создавать строку для каждого ::find.   -  person Steger    schedule 09.03.2014
comment
Нет @KitsuneYMG, я не думал ни о чем другом. Я задал вопрос здесь, потому что я не знаю, как это работает. Так что мне нужно, чтобы кто-то объяснил мне это, а не наказывал меня за то, что я не знаю того, о чем я спрашиваю. Если бы я знал, как это работает, зачем бы я спрашивал, как это работает?   -  person Byron Hawkins    schedule 09.03.2014


Ответы (1)


Это требует легковеса, на самом деле:

#include <boost/flyweight.hpp>
#include <map>

typedef boost::flyweight<std::string> string;

struct Bar
{
};

struct Foo {
    std::map<string, Bar*> bars;
};

int main()
{
    Foo foo;
    foo.bars.insert(string("hello"), new Bar());
    foo.bars.insert(string("world"), new Bar());
}

Теперь, чтобы обезопасить элементы Bar*, почему бы вам даже не использовать boost::ptr_map?

person sehe    schedule 09.03.2014
comment
Зачем тебе это делать? map не использует сравнение адресов, а строки легковесны. Кроме того, здесь нет тега c++11. в-третьих, поскольку ключи копируются только при первой вставке (в противном случае ключ остается один, и изменяется только значение), использование легковеса, как это, просто приводит к большому количеству дополнительных копий И вторая карта, содержащая все ключи, да? - person KitsuneYMG; 09.03.2014
comment
@KitsuneYMG Нет. Вы ошиблись в ожиданиях. Кроме того, там нет ничего специфичного для С++ 11. - person sehe; 09.03.2014
comment
using string = ... и emplace специфичны для С++ 11. - person KitsuneYMG; 09.03.2014
comment
Если вы хотите быть еще быстрее, посмотрите этот мой ответ Реализация «пула строк», который гарантированно не будет перемещаться (но гораздо менее универсальный и гибкий) - person sehe; 09.03.2014
comment
@KitsuneYMG Что ж, спасибо, что указали на это. c++11 — текущая версия языка. На самом деле мы в 2014 году, и все компиляторы поддерживают это. Если вопрос не помечен c++03, мы все предполагаем поддержку c++11 (возможно, не некоторые вещи что, например, MSVC не поддерживает) - person sehe; 09.03.2014
comment
@KitsuneYMG, я тоже не вижу тег c++03. Так что это за версия? - person Shoe; 09.03.2014
comment
@sehe И все же люди, которые задают вопросы, касающиеся новых функций, отмечают свои вопросы c++11. Создается впечатление, что большинство компиляторов еще не являются компиляторами C++, а новые возможности языка не используются большинством людей. От кого-то с такой высокой репутацией я ожидаю некоторого реального интеллекта и навыков рассуждения. Кроме того, часто двигаете стойки ворот? Там нет ничего конкретного для С++ 11? В самом деле? - person KitsuneYMG; 10.03.2014
comment
@KitsuneYMG Трололол. Хорошие у вас навыки рассуждения. Насколько я вижу, вы просто противопоставляете мои наблюдения своим собственным, а затем добавляете ad hominem. Зевота, субъективизм. Кроме того, в моем предложении действительно не было ничего c++11 конкретного. Мне потребовалось всего 7 секунд, чтобы изменить using на typedef и s/emplace/insert/. Снова зевни. (Зачем ты вообще здесь? Где твой ответ? Просто поставить минус и/или двигаться дальше?) - person sehe; 10.03.2014