Address of an instance emplaced to std::vector is invalid

263 views Asked by At

I have 2 std::vectors:

  • to first vector, I emplace instance
  • to second vector, I want to store the address of the instance just emplaced

But it does not work, i.e., the stored address differs from the emplaced instance's address.

If it matters at all, I'm on Linux and using g++ 5.1 and clang 3.6 with -std=c++11.

Here's a working example to illustrate the problem.

#include <iostream>
#include <vector>

struct Foo {
  Foo(int a1, int a2) : f1(a1), f2(a2) {}

  int f1;
  int f2;
};

int main(int, char**) {
  std::vector<Foo> vec1;
  std::vector<Foo*> vec2;

  int num = 10;
  for (int i = 0; i < num; ++i) {
    vec1.emplace_back(i, i * i);

    // I want to store the address of *emplaced* instance...
    vec2.push_back(&vec1.back());
  }

  // same
  std::cout << "size 1: " << vec1.size() << std::endl;
  std::cout << "size 2: " << vec2.size() << std::endl;
  // same for me
  std::cout << "back 1: " << &vec1.back() << std::endl;
  std::cout << "back 2: " << vec2.back() << std::endl;
  // typically differ ?
  std::cout << "front 1: " << &vec1.front() << std::endl;
  std::cout << "front 2: " << vec2.front() << std::endl;

  for (int i = 0; i < num; ++i) {
    std::cout << i + 1 << "th" << std::endl;
    // same for last several (size % 4) for me
    std::cout << "1: " << &vec1[i] << std::endl;
    std::cout << "2: " << vec2[i] << std::endl;
  }
}

Questions

  • Is it correct behavior ? I guess it's caused by storing the address of temporary instance but I want to know whether it's permitted by the standard (just curious).
  • If above is true, how to work around this ? I resolved this by changing first one to vector<unique_ptr<Foo>> but is there any idiomatic way ?
1

There are 1 answers

5
AudioBubble On BEST ANSWER

Two options:

1) You can simply fix your test. You just need in you test preallocate enough memory first with

 vec1.reserve(10);

Well, this is implementation details for std::vector. As more and more items are added to std::vector it needs to get more space for them. And this space must be contigious. So when there is not enough space for a new element std::vector allocates a bigger block of memory, copies existing elements to it, add the new element and finally frees the block of memory that it used before. As a result addresses that you stored in vec2 might become invalid.

However, if you preallocate enough memory for 10 elements then you code is correct.

Or, since reserving memory is sort of tricky thing to do

2) use std::deque since insertion and deletion at either end of a deque never invalidates pointers or references to the rest of the elements (http://en.cppreference.com/w/cpp/container/deque) and forget about the problem with invalidated addresses. So no need to reserve memory.