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

I have a symbol table implemented as a std::map. For the value, there is no way to legitimately construct an instance of the value type via a default constructor. However if I don't provide a default constructor, I get a compiler error and if I make the constructor assert, my program compile just fine but crashes inside of map<K,V>::operator [] if I try to use it to add a new member.

Is there a way I can get C++ to disallow map[k] as an l-value at compile time (while allowing it as an r-value)?


BTW: I know I can insert into the map using Map.insert(map<K,V>::value_type(k,v)).


Edit: several people have proposed solution that amount to altering the type of the value so that the map can construct one without calling the default constructor. This has exactly the opposite result of what I want because it hides the error until later. If I were willing to have that, I could simply remove the assert from the constructor. What I Want is to make the error happen even sooner; at compile time. However, it seems that there is no way to distinguish between r-value and l-value uses of operator[] so it seems what I want can't be done so I'll just have to dispense with using it all together.


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.