views:

98

answers:

3

I have created a container for generic, weak-type data which is accessible through the subscript operator.

The std::map container allows both data access and element insertion through the operator, whereas std::vector I think doesn't.

What is the best (C++ style) way to proceed? Should I allow allocation through the subscript operator or have a separate insert method?

EDIT

I should say, I'm not asking if I should use vector or map, I just wanted to know what people thought about accessing and inserting being combined in this way.

+2  A: 

In the case of Vectors: Subscript notation does not insert -- it overwrites.

This rest of this post distils the information from item 1-5 of Effective STL.

If you know the range of your data before hand -- and the size is fixed -- and you won't insert at locations which has data above it -- then you can use insert into vectors without unpleasant side-effects.

However in the general case vector insertions have implications such as shifting members upward and doubling memory when exhausted (which causes a flood of copies from the old vector's objects to locations in the new vector ) when you make ad hoc insertions. Vectors are designed for when you know the locality characteristics of your data..

Vectors come with an insert member function... and this function is very clever with most implementations in that it can infer optimizations from the iterators your supply. Can't you just use this ?

If you want to do ad-hoc insertions of data, you should use a list. Perhaps you can use a list to collect the data and then once its finalized populate a vector using the range based insert or range based constructor ?

Hassan Syed
To nitpick, in vectors of objects it isn't in general possible to `memcpy()` when growing a vector. It's necessary to call the copy constructor.
David Thornley
@David (+1) You are indeed correct, I have updated the text (I tend to abstract away). Copy Constructors are even worse then memcpy :D
Hassan Syed
`std::map::operator[]` returns the value if the key exists, otherwise it *inserts* a default object for the key and returns that. Since it is returned by reference you can overwrite it.
UncleBens
@UncleBens (+1) Thanks, I mean that only for vectors -- I will update.
Hassan Syed
+1  A: 

it depends what you want. A map can be significantly slower than a vector if you wish to use the thing like an array. A map is very helpful if the index you want to use is non-sequential and you have LOADS of them. Its usually quicker to just use a vector, sort it and do a binary search to find what you are after. I've used this method to replace maps in tonnes of software and I still haven't found something where it was slower to do this with a vector.

So, IMO, std::vector is the better way, though a map MIGHT be useful if you are using it properly.

Goz
A: 

Separate insert method, definitely. The operator[] on std::map is just stupid and makes the code hard to read and debug. Also you can't access data from a const context if you're using a operator[] to insert (which will lead to un-const-cancer, the even-more evil cousin of const-cancer).

Viktor Sehr
"Look-up and update if key exists, else insert default and update" is something I always find myself fighting over with Python's dict. `std::map::operator[]` has a very clear purpose. And if it didn't work that way, what would happen if the key didn't exist? Simply undefined behavior? So how would it be usable at all? Are you saying there shouldn't be a otherwise useful method, because it isn't useful if you only want to do look-up? Come on, the language even protects you from using it incorrectly for lookup in constant contexts.
UncleBens
I agree that there are some sort of protection as it can't be used in a const context. Still, yes, as with std::vector\deque or any other container with a [] operator, using it means you're sure the index\key exists, otherwise, use the map::find function.
Viktor Sehr
Unlike a vector/deque where I know "keys" 0...size exist, with a map there is no way to know which keys exist (unless you store all keys someplace else - of course you could test with find(), but then it would be pointless to use [] to access the value). I don't know about you, but I use map's index operator if I don't care whether the key exists, because adding a default is precisely what I want.
UncleBens