Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using std::map<K,V> where V has no usable default constructor

You can't make the compiler differentiate between the two uses of operator[], because they are the same thing. Operator[] returns a reference, so the assignment version is just assigning to that reference.

Personally, I never use operator[] for maps for anything but quick and dirty demo code. Use insert() and find() instead. Note that the make_pair() function makes insert easier to use:

m.insert( make_pair( k, v ) );

In C++11, you can also do

m.emplace( k, v );
m.emplace( piecewise_construct, make_tuple(k), make_tuple(the_constructor_arg_of_v) );

even if the copy/move constructor is not supplied.


Use map<K,V>::at(). map<K,V>::operator [] will try to default-construct an element if the key provided does not already exist.


Your V doesn't have a default constructor, so you cannot really expect std::map<K,V> std::map<K,V>::operator[] to be usable.

A std::map<K, boost::optional<V> > does have a mapped_type that is default-constructible, and likely has the semantics you want. Refer to the Boost.Optional documentation for details (you will need to be aware of them).


If the value-type is not default-constructible, then operator[] just won't work for you.

What you can do, though, is to provide free functions that get and set values in a map for convenience.

E.g:

template <class K, class V>
V& get(std::map<K, V>& m, const K& k)
{
    typename std::map<K, V>::iterator it = m.find(k);
    if (it != m.end()) {
        return it->second;
    }
    throw std::range_error("Missing key");
}

template <class K, class V>
const V& get(const std::map<K, V>& m, const K& k)
{
    typename std::map<K, V>::const_iterator it = m.find(k);
    if (it != m.end()) {
        return it->second;
    }
    throw std::range_error("Missing key");
}

template <class K, class V>
void set(std::map<K, V>& m, const K& k, const V& v)
{
    std::pair<typename std::map<K, V>::iterator,bool> result = m.insert(std::make_pair(k, v));
    if (!result.second) {
        result.first->second = v;
    }
}

You might also consider a getter like dict.get(key [, default]) in Python (which returns the provided default if key is not present (but that has a usability problem in that the default always has to be constructed, even if you know that key is in the map).


Derive a new class from std::map<K,V> and create your own operator[]. Have it return a const reference, which can't be used as an l-value.