1

I have the following type:

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

which is just a map to some domain object, using std::strings as keys. Now, std::string can be constructed and compared with const char*. (no need for an explicit std::string temporary, although maybe a implicit conversion is happening?)

The problem happens when I try to do something like

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

My main concern here is that it seems a little bit overkill to build a std::string just to make a comparison against an inmutable std::string, because the std::string temporary will want to have its own buffer, copy the const char* content in it, and then use that to run the find() method.

Is there a shortcut I can use to avoid the creation of a std::string temporary in here?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
lurscher
  • 25,930
  • 29
  • 122
  • 185

6 Answers6

1

Of course. Redeclare your function like this:

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

Now use std::string in the calling code right from the start.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Kerrek, so if i infer correctly from what you wrote, you would say that your answer to my question is "don't bother to avoid the temporary, passing a const std::string might hint the compiler to through magical means to avoid the buffer allocation"? – lurscher Feb 25 '12 at 13:11
  • @lurscher: No magic. You pass the string by const reference, not by value, so you won't be copying anything, and instead refer to an existing string. Of course you have to have an existing string elsewhere. – Kerrek SB Feb 25 '12 at 13:29
  • 2
    @lurscher - Kerrek's way to avoid the temporary is to not have a `const char*` parameter in the first place. If you use `std::string` everywhere, you don't need any conversions! – Bo Persson Feb 25 '12 at 13:34
  • maybe what i want is a type that is some sort of variant of a std::string and a inmutable string-like type with just a ref to const char* (user is responsible of the lifetime of const char*) – lurscher Feb 25 '12 at 13:41
  • 1
    @lurscher: Maybe. Who are we to tell your mind... but then you need to key the map on that type, and not on `std::string`. The map owns its key objects. If you don't like that, you may need a different container. – Kerrek SB Feb 25 '12 at 13:47
1

Main problem is declare of your container.

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

If look to source we will see :

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

So, you have strong restrictions by interface. Method find always use Key type as parameter and you can't change it without changing container key type.

If you sure that lose too many time on initialization of std::string you can use buffer (if no threads). For example :

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);
    }
};

Now, buffer allocates memory only once in constructor, not every time when call find.

Other way, use your own key type which can work std::string and const char *, but at this case you should to define implementation of Hash(boost::hash<Key>), predicate ( std::equal_to<Key>) with your Key type.

Something like this :

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(); }
};

Now, you have one way to get const char * and use it in hash and predicate. When you insert string you prefer to use StringKey. When find - CharKey.

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

But, at this case added virtual functions and creating Key objects might reduce perfomance and working with objectContainer became non-comfortable.

Torsten
  • 21,726
  • 5
  • 24
  • 31
0

//<---build error

What error? It should compile as-is. Unfortunately there's no simple way to do what you'd like. I've been bitten by the same issue. :(

There are multiple problems: parameter type of find(), types of std::equal_to, std::hash and std::less

XTF
  • 1,091
  • 1
  • 13
  • 31
0

You could run through all the keys one by one, and compare them with old strcmp if you really did not want to create any extra std::string. But Kerrek solution is the best in my opinion

qdii
  • 12,505
  • 10
  • 59
  • 116
  • the best solution for what? i'm asking if there is a way to avoid the temporary. a one by one key comparison defeats entirely the point of an unordered_map, which should have O(1) access – lurscher Feb 25 '12 at 13:13
0

You could inherit the boost::unordered_map and add a find with a version for "char *" that handles the temp std::string for you.

//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;

Also take the above with a grain of salt as I have not tested that. My current setup VS2008 with boost 1.40 does not have an issue with feeding a const char * to find (I am also not using auto). It does have an issue with const char * and the operator[] function to which I have done something similar for the operator[] function allowing access like objectContainer["key"]

krazer
  • 1
-2

Try using this :-

   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
  }
Invictus
  • 4,028
  • 10
  • 50
  • 80