Reputation: 111
So it's been a while since I did anything with c++ so please help me out on this issue.
I have a struct that I declare in a scope and then put in a map. How come this instance is still there outside of the scope. Shouldn't the allocated instance be deallocated?
#include <iostream>
#include <map>
#include <iterator>
struct foo {
int key;
int val;
};
int main() {
std::map<int,foo> map;
for (int i=0; i<10; i++) {
foo f; // allocated 1 time only?
f.key = 1000 + i;
if (i%2==0) {
f.val = i;
}
map.insert(std::make_pair(i, f)); // f is copied into the pair?
}
for (std::map<int, foo>::iterator it = map.begin(); it != map.end(); ++it) {
std::cout << it->first;
std::cout << " :: ";
std::cout << it->second.key;
std::cout << " -> ";
std::cout << it->second.val;
std::cout << std::endl;
}
}
this will generate the following
0 :: 1000 -> 0
1 :: 1001 -> 0
2 :: 1002 -> 2
3 :: 1003 -> 2
4 :: 1004 -> 4
5 :: 1005 -> 4
6 :: 1006 -> 6
7 :: 1007 -> 6
8 :: 1008 -> 8
9 :: 1009 -> 8
Is the comments in the code correct?
Now, say I would like to only instanciate val every other time, and the even times i would like it to be 0 (default). What would be the safest and most efficient way of doing this?
Upvotes: 0
Views: 127
Reputation: 7644
Containers hold copies of the values, and these copies will have the same lifetime as the container. This is called 'value-semantics', and is a key component of C++
#include <iostream>
#include <vector>
struct SomeType {
int val;
};
int main()
{
using namespace std;
{
int a = 3;
int b = a;
a = 4;
cout << b << endl; // this prints 3, int is a value type
}
{
SomeType a = {3};
SomeType b = a;
a.val = 4;
cout << b.val << endl; // this prints 3, SomeType is a value type
}
{
vector<int> a = {3};
vector<int> b = a;
a[0] = 4;
cout << b[0] << endl; // this prints 3, vector is a value type
}
}
As you can see, C++ follows value-semantics (almost) everywhere.
The only exceptions are thing like std::shared_ptr
, which are
designed to use reference/pointer semantics instead.
In general, when designing types in C++, do what int
s do.
Regarding your updated question, foo
is default-constructed 10 times
(in the for loops body), copy constructed ten times (while creating pairs),
lastly move-constructed ten times (while inserted into the container).
Your code fails to initialize foo::val
half of the time.
When I compile it on https://coliru.stacked-crooked.com/
I get:
main.cpp: In function 'int main()': main.cpp:18:35: error: 'f.foo::val' may be used uninitialized in this function [-Werror=maybe-uninitialized] map.insert(std::make_pair(i, f)); // f is copied into the pair?
(compiled with std=c++17 -O2 -Wall -Wextra -Werror -pedantic)
Failing to initialize a variable, and still reading it is UB (Undefined Behavior), so anything can happen;
To get the output you requested, and fewer copies, try:
#include <iostream>
#include <map>
struct foo {
int key;
int val;
};
int main() {
std::map<int,foo> map;
int val = 0;
for (int i=0; i<10; i++) {
int key = 1000 + i;
if (i%2==0)
val = i; // not doubled
map.emplace(i, foo{key, val});
}
for (auto&& item : map) {
std::cout << item.first << " :: " << item.second.key;
std::cout << " -> " << item.second.val << std::endl;
}
}
Upvotes: 1