избегать временного std :: string для вызова boost :: unordered_map :: find - PullRequest
1 голос
/ 25 февраля 2012

У меня есть следующий тип:

 boost::unordered_map< std::string , Domain::SomeObject > objectContainer;

, который является просто отображением некоторого доменного объекта, используя std::strings в качестве ключей.Теперь std::string можно построить и сравнить с const char*.(нет необходимости в явном std::string временном, хотя, может быть, происходит неявное преобразование?)

Проблема возникает, когда я пытаюсь сделать что-то вроде

void findStuff(const char* key) {
  auto it = objectContainer.find(key); //<---build error
}

Моя главная проблема здесьчто кажется немного излишним создание std::string просто для сравнения с неизменяемым std::string, потому что временный std::string захочет иметь свой собственный буфер, скопировать в него содержимое const char*, а затемиспользуйте это для запуска метода find().

Есть ли ярлык, который я могу использовать, чтобы избежать создания временного std::string здесь?

Ответы [ 6 ]

2 голосов
/ 25 февраля 2012

Конечно.Повторно объявите вашу функцию следующим образом:

void findStuff(std::string const & key);

Теперь используйте std::string в коде вызова с самого начала.

0 голосов
/ 02 мая 2012

// <--- ошибка сборки </p>

Какая ошибка?Он должен компилироваться как есть.К сожалению, нет простого способа сделать то, что вы хотите.Я был укушен той же проблемой.: (

Существует несколько проблем: тип параметра find(), типы std::equal_to, std::hash и std::less

0 голосов
/ 03 марта 2012

Основной проблемой является объявление вашего контейнера.

boost::unordered_map< std::string , Domain::SomeObject > objectContainer;

Если посмотреть на источник, мы увидим:

template<typename Key, typename Mapped, ...> 
    class unordered_map;
iterator find(const Key &);

Итак, у вас есть строгие ограничения по интерфейсу.Метод find всегда использует тип ключа в качестве параметра, и вы не можете изменить его, не изменив тип ключа контейнера.

Если вы уверены, что теряете слишком много времени при инициализации std :: string, вы можете использовать буфер (если нет потоков).Например:

class objectContainer : public boost::unordered_map<std::string, SomeObject>
{
    std::string _buffer;
public:
    typedef boost::unordered_map<std::string, SomeObject> inherited;

    objectContainer() { _buffer.reserve(1024); }

    typename inherited::iterator find(const char * key)
    {
        _buffer = key;
        return inherited::find(_buffer);
    }
};

Теперь буфер выделяет память только один раз в конструкторе, а не каждый раз, когда вызов находит.

Другой способ, используйте свой собственный тип ключа, который может работать с std :: stringи const char *, но в этом случае вам следует определить реализацию Hash (boost::hash<Key>), предикат (std::equal_to<Key>) с вашим типом Key.

Примерно так:

class Key 
{
public:
virtual ~Key();

virtual const char * key() = 0; // for hash and predicate
};

// predicate
struct equal_to_Key : binary_function <Key,Key,bool> {
    bool operator() (const Key & x, const Key & y) const
    { 
       return false; // TODO : compare Key here
    }
};

class CharKey : public Key
{
const char * _key;
public:
   virtual const char * key() { return _key; }
};

class StringKey : public Key
{
std::string _key;
public:
   virtual const char * key() { return _key.c_str(); }
};

Теперь у вас есть один способ получить const char * и использовать его в хеше и предикате.Когда вы вставляете строку, вы предпочитаете использовать StringKey.Когда находят - CharKey.

boost::unordered_map< Key , Domain::SomeObject, KeyHashFunctor, equal_to_Key > objectContainer;
void findStuff(const char* key) {
  auto it = objectContainer.find(CharKey(key));
}

Но в этом случае добавленные виртуальные функции и создание объектов Key могут снизить производительность, и работа с objectContainer стала неудобной.

0 голосов
/ 03 марта 2012

Вы можете унаследовать boost :: unordered_map и добавить поиск с версией для "char *", которая обрабатывает temp std :: string для вас.

//boost::unordered_map< std::string , Domain::SomeObject > objectContainer;
class ObjectContainer::public boost::unordered_map< std::string , Domain::SomeObject >
{
public:
    iterator find(const char *key){const std::string constKey(key); return boost::unordered_map< std::string , Domain::SomeObject >::find(constKey);}
    const_iterator find(const char *key)const {const std::string constKey(key); return boost::unordered_map< std::string , Domain::SomeObject >::find(constKey);}
};

ObjectContainer objectContainer;

Также возьмите вышеприведенное с зерномсоли, как я не проверял это.Моя текущая настройка VS2008 с boost 1.40 не имеет проблемы с подачей const char * для поиска (я также не использую auto).У него действительно есть проблема с const char * и функцией operator [], с которой я сделал нечто подобное для функции operator [], разрешающей доступ как objectContainer ["key"]

0 голосов
/ 25 февраля 2012

Попробуйте использовать это: -

   void findStuff(const char* key) 
   {
     std::string abc = (std::string)key; //<---build error
     auto it = objectContainer.find(abc); // Now use "abc to find in Map
  }
0 голосов
/ 25 февраля 2012

Вы можете запустить все ключи один за другим и сравнить их со старым strcmp, если вы действительно не хотите создавать дополнительную строку std :: string.Но решение Kerrek является лучшим на мой взгляд

...