0

As in the title I am trying to write a template class map that is build on vector of structs where I hold the key that is a string and a value of template TYPE. There is the short main program that represents the usage of my class:

int main()
{
  map<int> iv;

  iv["john"] = 23;

  int ia = iv["john"]++;
  int ib = iv["john"];

  cout << ia << " " << ib << endl; // prints 23 24
  try{
  int ic = iv["jack"];  // should throw an exception
  }catch(map<int>::Uninitialized&)
  {
    cout << "Uninitialized map element!" << endl;
  };
}

And there is the class i have managed to write:

class map
{
private:
  struct FIELD
  {
    string key;
    TYPE value;
  };
  vector<FIELD> data;
public:
  TYPE& operator[] (const string index)
  {
    typename vector<FIELD>::iterator idx;
    for(idx = data.begin(); idx != data.end(); ++idx)
    {
      if(idx->key == index)     return idx->value;
    }
    if(idx == data.end())
    {
      FIELD toAdd;
      toAdd.key = index;
      data.push_back(toAdd);
    }
    for(idx = data.begin(); idx != data.end(); ++idx)
    {
      if(idx->key == index) return idx->value;
    }
    return idx->value;
  }
};

It only works properly for assignment operations like `iv["john"] = 23; but when I try to read elements that are not initialized the operator[] creates new element that only contains the key and this is wrong. I know there is no such thing like checking if the value is uninitialized. The problem is that both writing and reading operation call operator[] and I don't quite understand how to throw an exception in this situation. I have looked all over the web and found that I can create two indexing operators, one for reading and one for writing - like this:

TYPE& operator[] (const string index)
TYPE operator[] (const stirng index) const;

and the compiler will know when to use which. But I guess it will not solve the problem at all.

  • In `std::map`, they have made it where `operator[]` always creates the new element automatically with a default-constructed `T`, and `at` is the one that will throw the exception if it is not there. – user3175411 Feb 01 '14 at 20:12
  • Yes, I know it, but how to implement the at() functionality on stl::vector where indexes are stl::string's ? – user3261146 Feb 01 '14 at 20:15
  • just put `throw` inside the `if(idx == data.end())` instead of adding a new one. – user3175411 Feb 01 '14 at 20:20
  • You don't have lots of choices here. Either (1) don't have `operator[]` that returns a reference, or (2) mimick `std::map` behaviour, or (3) throw an exception whenever a key is not found. `at()` never adds a new item to the vector, so you can easily replicate this behaviour with option 3. – n. m. could be an AI Feb 01 '14 at 20:24
  • 1
    The standard namespace is `std`, not `stl` (for the very good reason that the STL has absolutely nothing to do with it) – Lightness Races in Orbit Feb 01 '14 at 20:53

1 Answers1

1

Don't try to reimplement std::map, create an alias to it, instead:

template<class Type>
using map = std::map<std::string, Type>;

Remember not to import using namespace std;, since it is considered bad practice and you would have a name collision in this case.

And yes, std::map::operator[] always created an element inside the map. You can use std::map::at to make it throw an exception std::out_of_range if the element is not there, or check with map.find(key) == map.end().

Your main would become:

int main() {
    map<int> iv;
    iv["john"] = 23;

    int ia = iv["john"]++;
    int ib = iv["john"];

    std::cout << ia << " " << ib << std::endl; // prints 23 24
    try {
        int ic = iv.at("jack");
        //         ^^^^      ^
    } catch(const std::out_of_range&) {
    //      ^^^^^^^^^^^^^^^^^^^^^^^^
        std::cout << "Uninitialized map element!" << std::endl;
    };
}
Community
  • 1
  • 1
Shoe
  • 74,840
  • 36
  • 166
  • 272
  • Well, this would be better approach, but I am not allowed to change main program and has to build and mimick behaviour of std::map on different std containers. I've come up with an idea that I could create two indexing operators, one for reading and one for writing, then the one for writing would work like it works now and that one for reading would check if the index(string) exists and if not - throwing and exception.To do that I've also found that in that case I should create a helper class to differentiate between reading and writing. But I don't quite know how to implement it on my example – user3261146 Feb 02 '14 at 09:01