gartenzwerg
gartenzwerg

Reputation: 247

invoking constructors of struct in functions, C++

Here is the approximate code of my question. First, we have a struct with a constructor:

struct Pair{
Pair(int a, int b): (first (a) , second (b) ) {}
int first;
int second;
};

which is used in a map

map<string, Pair> mymap;

I would like to initialize this map in a function

void f(map<string, Pair>* mymap, string c,int x, int y )
{
(*mymap)[c]=Pair(x,y);
}

But the compiler says first that it could not find an appropriate constructor and then the next lines are that not enough arguments are provided for the constructor.

A friend of mine told me that I should write the function like this:

void f(map<string, Pair>& mymap, const string& c,int x, int y )
{
  if (mymap.find(c) != mymap.end()) {
    mymap[c] = Pair(x,y);
   }
}

But he could not explain why Type& should be used here instead of Type *, and I would like to clarify this point. Can anybody explain?

Upvotes: 1

Views: 222

Answers (4)

juanchopanza
juanchopanza

Reputation: 227558

You will need a default contrructor:

Pair(): first () , second () {}

This is needed for the map's operator[], which creates a default-constructed mapped_type when called with a non-existent key-

and a less-than operator that implements strict weak ordering:

struct Pair {
  // as before
  bool operator<(const Pair& rhs) const {
     / some code to implement less-than
  }
};

or you can pass a comparison functor or function implementinf strict weak ordering as a third template argument.

Upvotes: 2

The problem is that operator[] in a map requires that the value type is default constructible. If you don't want your Pair to be default constructible, you will have to avoid using operator[]:

void f(map<string, Pair>& mymap, string c,int x, int y )
{
   mymap.insert( std::make_pair(c,Pair(x,y)) );
}

You may have misunderstood what your friend suggested. The problem with operator[] is not that it requires the default constructor if it needs to create a new element, but that it requires it in case it might need to. That is, whether the element exists before hand or not does not really matter.


If you also mean to update, then you need to consider also that option:

void f(map<string, Pair>& mymap, string c,int x, int y )
{
   auto res = mymap.insert( std::make_pair(c,Pair(x,y)) );
   if ( !res.second )
      res.first->second = Pair(x,y);
}

Basically the insert operation returns a pair of the iterator pointing at the key and a bool indicating if this insert created the object or if it was already there (in which case the value in the map is unmodified). By storing the result, we can test and if the insert did not create the value, we can update it through the returned iterator.

Upvotes: 3

pmr
pmr

Reputation: 59841

Calling operator[] on map will require your type to be default constructible. You can avoid that by using map::insert or map::emplace

Upvotes: 2

Mark Ransom
Mark Ransom

Reputation: 308520

The standard containers need a default constructor. They will use the operator= to set the correct value at some point after construction.

Upvotes: 0

Related Questions