0

my C++ is super rusty, how come the locally declared 'out' map still works outside it's scope? Is it legit? Shouldn't it be dynamically declared with "new"?

// Example program with a function returning a map
#include <iostream>
#include <string>
#include <map>

std::map<std::string, int> function()
{
    std::map<std::string, int> out;
    out["one"] = 1;
    out["two"] = 2;
    std::cout << &out << std::endl;
    return out;
}


int main()
{
  std::map<std::string, int> out1 = function(); 
  std::cout << &out1 << std::endl;
  for (const auto & iter : out1)
    std::cout << iter.first << " = " << iter.second << std::endl;
}

When I run it:

0x7ffd89f354a0
0x7ffd89f354a0
one = 1
two = 2

It seems like they point to the same memory address

O. San
  • 755
  • 2
  • 6
  • 18
  • Where is it not in its scope? – Iter Ator Jul 20 '20 at 07:28
  • if `function()` returned a map pointer it would be out of scope in main(), but it isn't: it's returning it by value. – Rup Jul 20 '20 at 07:30
  • @IterAtor - It's declared in function "function" and used in main, no? – O. San Jul 20 '20 at 07:30
  • 3
    Side note - "Shouldn't it be dynamically declared with "new"?". No, very few things should these days. – George Jul 20 '20 at 07:32
  • @O.San No. Both `out` in your code refer to different map objects. – Daniel Langr Jul 20 '20 at 07:36
  • Consider this `int func() { int n = 3; return n; }` Do you worry that `n` is being used outside of it's scope? You shouldn't worry about your function either. – john Jul 20 '20 at 07:41
  • You're getting confused between values and references. You are thinking that there's map declared in `function` which is destroyed when the `function` is exited. Both these things are true. But you are also thinking that you are returning some kind of reference to the map declared in `function`. If that was true then there would be a problem, but it isn't. Instead a copy of being made of the map (before it is destroyed) and that copy is returned and assigned to your variable in `main`. Exactly the same thing happens when you return an `int` from a function. – john Jul 20 '20 at 07:47
  • For clarity the copy being made of the map is notional. A compiler may optimise away that copy, as it seems it has in your case. – john Jul 20 '20 at 07:51

2 Answers2

3

You're returning a std::map<> by value through the function function() and then it's assigned in the variable out in the main() and you're able to print them correctly. That's it.

And remember, you can't access the out of function() directly outside of the function since they're local, you're applying the values written in function() in a function call during assignment in main().

Here's what you're wondering:

int function() {
    int a = 10;
    int b = 20;
    int x = a + b;
    return x;
}

int main(void) {
    // Oh! It could access the local variable of 'function()'
    int result_x = function();

    // But no, the following won't work:
    int result_x_incorrect = a; // 'a' declared in 'function()' scope

    return 0;
}
Rohan Bari
  • 6,523
  • 3
  • 9
  • 30
  • I Should've given better names to the maps, I updated them to distinct with "out" and "out1". What I don't understand that I remember that if I need a map for instance to live outside the scope (aka, be on the heap instead of the stack) it needs to be dynamically allocated, but here it declared locally. Automated var if you will – O. San Jul 20 '20 at 07:50
  • @O.San But in your case, the point is that you _don't need a map to live outside of the scope_. You move the contents of the map declared in `function` to a map declared in `main`. – Daniel Langr Jul 20 '20 at 07:53
  • When you return a value from a function you are effectively copying it from one scope to another. There are (notionally at least) two maps in your program and neither is being used outside its scope. However from the addresses you've printed, in your case it seems that compiler has been smart and optimised one copy away. Either way your code is perfectly correct. – john Jul 20 '20 at 07:54
  • But printing &out and &out1 shows the same address... I'm missing something here – O. San Jul 20 '20 at 07:56
  • Compiler optimisation is what you're missing. C++ goes to great lengths to allow compiler to reduce the amount of copying that happens. But in turns of understanding the correctness of the code you should still think in terms of the map being copied. – john Jul 20 '20 at 07:57
  • @O.San That's irrelevant. It may happen due to compiler optimizations. Anyway, both `out` and `out1` are still different maps at the source code level. – Daniel Langr Jul 20 '20 at 07:57
  • @O.San It's really not hard to understand, just think about integers. Have you ever worried that when you return a int variable from a function that you will end up using that variable outside it's scope? C++ is designed to allow complex objects to be handled exactly the same way as primitive types like integers. – john Jul 20 '20 at 08:00
  • BTW, note that, again at a source code level, storage durations of `out` and `out1` do not overlap. `out1` is initialized-constructed from a temporary at a moment where `out` has already been destroyed. – Daniel Langr Jul 20 '20 at 08:09
  • I would guess that your compiler used std::move (https://en.cppreference.com/w/cpp/utility/move) to return your map from the function to main. – SKCoder Jul 20 '20 at 09:34
  • @SKCoder In fact, using `return` with `std::move` for local non-static variables [can avoid elision](https://stackoverflow.com/q/14856344/580083). The compiler did not use `std::move` here, it just treated `opt` as rvalue, because the Standard says so. – Daniel Langr Jul 20 '20 at 11:34
2

the function function returns a copy from the local map which in the function scope.

Both copies dynamically allocate different elements.

In main the function function returns a copy and destroys the local map but since c++11, the function moves the map.

This is similar to when using

std::vector vec1 {1, 2};
auto vec2 = vec1;

Now every vector allocates different elements in the heap.

asmmo
  • 6,409
  • 1
  • 7
  • 22
  • IIRC, `return out;` causes _move_ instead of _copy_ since C++11 and this is _not_ a compiler optimization. The C++ Standard says that `out` must be treated as an _rvalue_ in the `return` statement here. – Daniel Langr Jul 20 '20 at 07:45
  • @DanielLangr It is possible that NVRO is happening here isn't it as most compilers do that by default? If there is no compiler optimization, then yes the map will be moved since it has a move constructor. – jignatius Jul 20 '20 at 09:49
  • @jignatius Yes, NRVO does happen here almost for sure. But these are orthogonal problems. NRVO elides move constructor, which is called due to `opt` being treated as rvalue. – Daniel Langr Jul 20 '20 at 11:28