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 constructo
It's a bit ugly, but one way to work around this is to add a member variable that tracks whether an instance is valid or not. Your default constructor would marks an instance as being invalid but all your other constructors mark the instance as valid.
Make sure your assignment operator properly transfers the new member variable.
Modify your destructor to ignore invalid instances.
Modify all your other member functions to throw/error/assert when they operate on an invalid instance.
You can then use your object in a map and as long as you only use objects that were properly constructed, your code will work fine.
Again, this is a workaround if you want to use the STL map and are not willing to use insert and find instead of operator[].
Use map<K,V>::at()
. map<K,V>::operator []
will try to default-construct an element if the key provided does not already exist.
you could specialize std::map for your value-type. I'm not saying it's a good idea, but it can be done. I specialized scoped_ptr<FILE>
's dtor to fclose
instead of delete
.
Something like:
template<class K, class Compare, class Allocator>
my_value_type& std::map<K,my_value_type,Compare,Allocator>::operator[](const K& k)
{
//...
}
This should allow you to insert the code you want into operator[] for your type. Unfortunately, I do not know of a way in current c++ to return only r values. In c++0x you might be able to use:
template<class K, class Compare, class Allocator>
my_value_type&& std::map<K,my_value_type,Compare,Allocator>::operator[](const K& k)
{
//...
}
This will return an R-value reference (&&).
When you use an operator override in C++, it's best to stick as closely as possible with the semantics of the operator in the default case. The semantics of the default. operator[] is replacement of an existing member in an array. It would appear that std::map bends the rules a bit. That's unfortunate, because it leads to this sort of confusion.
Note that the documentation (http://www.sgi.com/tech/stl/Map.html) for operator[] under std::map says:"Returns a reference to the object that is associated with a particular key. If the map does not already contain such an object, operator[] inserts the default object data_type()."
I'd suggest that you treat replacement and insertion differently. Unfortunately, this means you need to know which is required. That may mean doing a lookup on the map first. If performance is an issue, you might need to find an optimization where you can test for membership and insert with one lookup.
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).
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).