ellipticaldoor
ellipticaldoor

Reputation: 1272

How I can safely reference to an std::vector element?

I will like to create a struct that holds a higher state for other structs.

I tried to create it like this, but I recently found that returning a pointer to an element of an std::vector is not safe, since that pointer can change.

struct Foo {
  std::string &context;
  std::string content;

  Foo(std::string &context, std::string content) : context(context), content(content) {}
}

struct Bar {
  std::string context;
  std::vector<std::string> manyFoos;

  Foo* addFoo(std::string content) {
    manyFoos.emplace_back(context, content);
    return &manyFoos[manyFoos.size() - 1];
  }
}

struct Example {
  Bar bar;
  Foo* fooA;
  Foo* fooB;

  Example() {
    fooA = bar.addFoo("Hello");
    fooB = bar.addFoo("World");
  }
}

What could be a good and safe way of doing this?

Upvotes: 1

Views: 693

Answers (2)

Rushikesh Talokar
Rushikesh Talokar

Reputation: 1585

You can have vector of shared_ptr & return weak_ptr, this way you can make sure correct referencing

#include <iostream>
#include <vector>
#include <memory>

struct Foo {
    std::string &context;
    std::string content;

    Foo(std::string &context, std::string content) : context(context), content(content) {}
};

struct Bar {
    std::string context;
    std::vector<std::shared_ptr<Foo>> manyFoos;

    std::weak_ptr<Foo> addFoo(std::string content) {
        auto foo = std::make_shared<Foo>(context, content);
        manyFoos.emplace_back(foo);
        return foo;
    }
};

void printFoo(std::weak_ptr<Foo> foo)
{
    // Here we are checking weak_ptr expiry
    std::cout << (foo.expired() ? "Foo Expired" : foo.lock()->content) << std::endl;
}

int main() {
    Bar bar;
    std::weak_ptr<Foo> fooA;
    std::weak_ptr<Foo> fooB;

    fooA = bar.addFoo("Hello");
    fooB = bar.addFoo("World");

    printFoo(fooA);
    printFoo(fooB);

    // erase last element
    bar.manyFoos.pop_back();
    printFoo(fooB);
    return 0;
}

Output:

Hello
World
Foo Expired

Upvotes: 1

YSC
YSC

Reputation: 40100

What could be a good and safe way of doing this?

Saving the couple (vector reference, Foo's index), assuming Foos are only added at the back.

With a bit of syntaxic sugar:

struct Bar {
  std::string context;
  std::vector<Foo> manyFoos;

  struct FooProxy
  {
    std::vector<Foo>& foos;
    std::vector<Foo>::size_type index;
    operator Foo() { return foos[index]; }
  };

  auto addFoo(std::string content) {
    manyFoos.emplace_back(context, content);
    return FooProxy{manyFoos, manyFoos.size()-1};
  }
};

Live example

Upvotes: 2

Related Questions