Thobias Bergqvist
Thobias Bergqvist

Reputation: 111

What is the scope of a statically declared struct in c++

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

Answers (1)

sp2danny
sp2danny

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 ints 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

Related Questions