C++ pass STL container items as reference - c++

I have a map of type std::map<std::string, std::vector<MyClass>>. The map is filled in this way that I create a vector and put it with a guid as a pair into the map. Then I want to call a function, give the just inserted vector to it and let it fill the vector. It looks like that:
{
std::string guid = "aGUID"
std::vector<MyClass> vec_myClass(0);
my_map[guid] = vec_myClass;
std::vector<MyClass>& vec_ref = my_map[guid];
FillVector(vec_ref);
}
FillVector(std::vector<MyClass>& vec) { vec.push_back(...); }
I think the [] operator returns a reference of the item in my_map, which I can give to a function to work with it, but my application crashes at this point. I am putting the vector first into the map (when it is empty) because I want to avoid copying effort as function FillVector puts lots of items into the vector. Where is my mistake? Might it be wrong to pass a reference by reference to a function? Or is there a clearly better solution to this? I prefer references over pointers here. Thx, and all the best.

All that code simplifies to:
{
std::string guid = "aGUID"
FillVector(my_map[guid]);
}
Btw. I think your problem does not appear to be here, but in code you don't show us...

std::map operator would create value for the key internally if it does not exist. see this link. passing the reference to function is ok, the problem seem to be somewhere else in your code.

Related

Why can't I insert objects into a map in c++

So i have a function that is supposed to return a following map:
map<MyObj*, Datastruct>
While trying to insert object from a list which contains said objects:
for(auto element: myList){
Datastruct str;
str.property1 = element.property1;
str.property2 = element.property2;
saidMap.insert(element.PointerToMyObj, str);
}
Objects in myList contain a pointer to myObj and some properties i need to move from list to a map. Having executed this code in a function below:
map<MyObj*, Datastruct> listToMap = convertList(myList);
I get yelled at by the compiler that:
"no matching function to call to 'std::map::insert(MyObj*&, Datastruct&)"
Here i am helpless. I don't know why compiler would show that i am trying to pass a reference to the function if element in myList(see above) contains
MyObj *PointerToMyObj;
which, the way i see it, is a correct type to pass to the insert function right?
I also tried with
std::make_pair
whereupon compiler yells at me for trying to insert a pair into a
map<MyObj*, Datastruct>.
I am utterly lost. Could someone explain to me what i am doing wrong?
The map's value type is not
std::pair<MyObj*, Datastruct>
but
std::pair<MyObj* const, Datastruct>
Presumably you hardcoded the pair type somewhere, but missed out the const.
Or, if you wrote saidMap.insert(std::make_pair(element.PointerToMyObj, str)) then that should have worked and something else is wrong in your code.
But it's much easier to use emplace:
saidMap.emplace(element.PointerToMyObj, str);
This C++11 version of insert has all the magic machinery needed to make that work "transparently".
Your code is almost alright. You're getting this compiler error because map::insert really doesn't have such overload. It's a tiny detail you've missed on reading up when looking at the std::map documentation. It is so that map::insert doesn't work as implicit constructor, which is how you are trying to use it in the pasted code.
This is something you can achieve by emplacing - map::emplace:
for(auto element : myList){
Datastruct str;
str.property1 = element.property1;
str.property2 = element.property2;
saidMap.emplace(element.PointerToMyObj, str);
}
Using map::insert(): The insert function is used to insert the key-value pair in the map and has 3 general overloads, in fact, they are more, but these are the main ideas behind how to insert in a map.
insert(pair): simply inserts a new pair in the map, where pair.first is the key and pair.second is the value. Only happens when the key is not already in the map.
insert(it, pair): insert using an iterator and a pair, where it is a pointer to the location where you want to insert your pair at.
insert(begin, end): used for copying the elements from another map by accepting iterators to begin and end of the map.
In your case, you've missed the to actually pass a pair. You can construct it inside of the insert argument list as follows:
for(auto element : myList){
Datastruct str;
str.property1 = element.property1;
str.property2 = element.property2;
saidMap.insert( /*implicitly derive a pair as:*/ { element.PointerToMyObj, str } );
}
Note: This is a post-C++11 functionality, so make sure you're setting compiler's C++ version to at least that.
I've reproduced your code with the working version of the map::insert in Compiler Explorer (godbolt) for you have a look (in both gcc and clang compilers): https://godbolt.org/z/7Mqut0.
Hope this helps!

C++ passing by reference in constructor

So I am making a program in C++ and my main method has to construct an object that takes as a parameter a vector.
This is my code:
int main() {
vector<Seller> *staff = new vector<Seller>;
for (int i = 0; i < 50; i++) {
staff->push_back(Seller(i));
}
BookStore store(*staff);
deque<Book> books;
books = store.getBooks();
}
So, these are some pretty simple Object-Oriented concepts I think.
My goals are:
First, initializing an empty vector of sellers. A Seller is an object that has a constructor:
Seller(int i);
And represents, of course, a seller.
Then, I want to fill in the vector with actual Sellers. These are constructed in the for loop.
Then, I want to create a Store, which takes as an argument the sellers that work there.
Finally, I create a new deque called books, and I assign to it the value of books in the Store class. The initialisation of the Books deque is done in the constructor of the Store:
Store::Store(vector<Seller> &sellers) {
this->sellers = sellers;
this->books = deque<Book> (100, "Harry Potter");
}
So this is the code and I am wondering if I am making a mistake in the passing arguments to new constructors part.
I am a bit confused when passing by reference so I am asking for a bit on help on that part. I have two main questions:
1) Are there any errors there, considering how I want to run my program? Consider also that in the rest of the main method (not included here) I constantly change the value of the books deque.
2) Is there any way to replace an element in a deque without having to erase and insert?
Is there a built-in function replace? If not, is the below code going to work if I just want to replace a value in the deque?
For example, if the deque is like that:
3 4 5 2
And it (an iterator) has value 2.
Then I want the deque to become:
3 4 6 2
When doing:
books.erase(it);
books.insert(it, 6);
Thanks for any tips or help!
OK, here a short analysis.
Firstly, the unique real error I found: staff is defined as a pointer and is given a value with new but is never released. You should anyway avoid using raw pointers, so either create the object on the stack:
vector<Seller> staff{};
or use a smart pointer
auto staff = make_unique<vector<Seller>>{};
(you will then have to learn something about the ownership semantics, so as you still are a beginner I'd recommend the first solution).
Then, notice how the line
this->sellers = sellers;
in Store::Store will make a copy of the sellers vector, which probably is not what you meant. If you wanted your store to reference the variable created on main(), you should redefine your Store as
class Store {
// ...
vector<Seller>& sellers;
//...
};
and the constructor as
Store::Store(vector<Seller> &sellers) :
sellers{sellers} // reference member variables must be given a value before the body of the constructor begins
{
books = deque<Book> (100, "Harry Potter");
}
For the same reason, your line
books = store.getBooks();
will make a copy of the deque (but maybe in this case it was intended).
Finally, C++ offers many container manipulating functions under the <algorithms> library. Take a look at the reference. But if you already have an iterator to the element you want to replace, you do not need such algorithms, just write:
*it = 6;

C++11: std::unordered_map<int, std::stack<int>> getting values from map without copying multiple times

I have a global variable:
std::unordered_map<int, std::stack<int>> intTable;
To add to this, I do this currently: (I've seen C++11 initializer lists, but I'm not sure if I'll hit this Visual C++ 11 2013 bug --> http://connect.microsoft.com/VisualStudio/feedback/details/807966/initializer-lists-with-nested-dynamically-allocated-objects-causes-memory-leak)
std::stack<int> s;
s.push(10);
then I do
intTable[integerKey] = s;
I then need to add to the stack sometimes, and check its top value so, and if it is greater than a value, I need to pop it and erase the key from the map:
intTable[integerKey].push(20);
if (intTable[integerKey].top() >= someIntegerValue)
{
intTable[integerKey].pop();
if (intTable[integerKey]->size() == 0)
{
intTable.erase(integerKey);
}
}
My question is there a better way of doing this? For example one inefficieny I see is that I'm indexing into the map multiple times. Is it possible to avoid that? How can I store a reference to the intTable[integerKey] without copying it?
Map members are initialized with the default constructor when accessed.
std::stack<int> &localReference = intTable[integerKey];
Will allocate the stack (if it does not exist), and return a reference to the stack.
You can do
std::stack<int> &localReference = intTable[integerKey];
In beginning of your function and access local reference afterwards, although compiler will most likely optimize away intTable[integerKey] anyways within the scope of local function so it may be no difference in actual assembly code.
Unordered map access by key is really fast. You could take a reference to the element and work on that, but otherwise this is fine.

Adding to a vector of pair

I have a vector of pair like such:
vector<pair<string,double>> revenue;
I want to add a string and a double from a map like this:
revenue[i].first = "string";
revenue[i].second = map[i].second;
But since revenue isn't initialized, it comes up with an out of bounds error. So I tried using vector::push_back like this:
revenue.push_back("string",map[i].second);
But that says cannot take two arguments. So how can I add to this vector of pair?
Use std::make_pair:
revenue.push_back(std::make_pair("string",map[i].second));
IMHO, a very nice solution is to use c++11 emplace_back function:
revenue.emplace_back("string", map[i].second);
It just creates a new element in place.
revenue.pushback("string",map[i].second);
But that says cannot take two arguments. So how can I add to this vector pair?
You're on the right path, but think about it; what does your vector hold? It certainly doesn't hold a string and an int in one position, it holds a Pair. So...
revenue.push_back( std::make_pair( "string", map[i].second ) );
Or you can use initialize list:
revenue.push_back({"string", map[i].second});
Read the following documentation:
http://cplusplus.com/reference/std/utility/make_pair/
or
http://en.cppreference.com/w/cpp/utility/pair/make_pair
I think that will help. Those sites are good resources for C++, though the latter seems to be the preferred reference these days.
revenue.push_back(pair<string,double> ("String",map[i].second));
this will work.
You can use std::make_pair
revenue.push_back(std::make_pair("string",map[i].second));
Try using another temporary pair:
pair<string,double> temp;
vector<pair<string,double>> revenue;
// Inside the loop
temp.first = "string";
temp.second = map[i].second;
revenue.push_back(temp);
Using emplace_back function is way better than any other method since it creates an object in-place of type T where vector<T>, whereas push_back expects an actual value from you.
vector<pair<string,double>> revenue;
// make_pair function constructs a pair objects which is expected by push_back
revenue.push_back(make_pair("cash", 12.32));
// emplace_back passes the arguments to the constructor
// function and gets the constructed object to the referenced space
revenue.emplace_back("cash", 12.32);
As many people suggested, you could use std::make_pair.
But I would like to point out another method of doing the same:
revenue.push_back({"string",map[i].second});
push_back() accepts a single parameter, so you could use "{}" to achieve this!

STL map - insert or update

I have a map of objects and I want to update the object mapped to a key, or create a new object and insert into the map. The update is done by a different function that takes a pointer to the object (void update(MyClass *obj))
What is the best way to "insert or update" an element in a map?
The operator[]
With something like the following snippet:
std::map<Key, Value>::iterator i = amap.find(key);
if (i == amap.end())
amap.insert(std::make_pair(key, CreateFunction()));
else
UpdateFunction(&(i->second));
If you want to measure something that might improve performance you might want to use .lower_bound() to find where an entry and use that as a hint to insert in the case where you need to insert a new object.
std::map<Key, Value>::iterator i = amap.lower_bound(key);
if (i == amap.end() || i->first != key)
amap.insert(i, std::make_pair(key, CreateFunction()));
// Might need to check and decrement i.
// Only guaranteed to be amortized constant
// time if insertion is immediately after
// the hint position.
else
UpdateFunction(&(i->second));
something like:
map<int,MyClass*> mymap;
map<int,MyClass*>::iterator it;
MyClass* dummy = new MyClass();
mymap.insert(pair<int,MyClass*>(2,dummy));
it = mymap.find(2);
update(it.second);
here a nice reference link
The operator[] already does, what you want. See the reference for details.
The return value of insert is "a pair consisting of an iterator to the inserted element (or to the element that prevented the insertion) and a bool denoting whether the insertion took place."
Therefore you can simply do
auto result = values.insert({ key, CreateFunction()});
if (!result.second)
UpdateFunction(&(result.first->second));
NOTE:
Since your question involved raw pointers, and you said you wanted your Update function to take a pointer, I have made that assumption in my snippet. Assume that CreateFunction() returns a pointer and UpdateFunction() expects a pointer.
I'd strongly advise against using raw pointers though.
In C++17, function insert_or_assign insert if not existing and update if there.