33

I know STL containers copy the objects. So say I have a

list<SampleClass> l;

each time when I do

SampleClass t(...);
l.push_back(t);

a copy of t will be made. If SampleClass is large, then it will be very costly.

But if I declare l as a container of references,

list<SampleClass&> l;

When I do

l.push_back(t);

Will it avoid copying the objects?

David Nehme
  • 21,379
  • 8
  • 78
  • 117
CuriousMind
  • 15,168
  • 20
  • 82
  • 120

5 Answers5

42

If you know what you're doing, you can make a vector of references using std::reference_wrapper:

#include <functional>
#include <vector>

int main()
{
  std::vector<std::reference_wrapper<int>> iv;

  int a = 12;
  iv.push_back(a);  // or std::ref(a)

  // now iv = { 12 };

  a = 13;

  // now iv = { 13 };
}

Note of course that any of this will come crashing down on you if any of the referred-to variables go out of scope while you're still holding references to them.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 2
    Right: I think that `reference_wrapper`s should be preferred to raw pointers, in order to clearly communicate non-ownership, avoid the gnarly pointer syntax (in exchange for breaking `auto` and having to use `.get()` sometimes...), and avoid the possibility of uninitialised pointers or `nullptr` or accidentally doing pointer arithmetic or so on. – underscore_d Sep 30 '18 at 12:48
24

Sadly no, it won't compile (with stlport at least). But the alternative, which is to store pointers to your objects in the container, will compile perfectly fine.

This will leave you with a bit of extra syntactic noise around your code - you'll have to new things in order to insert them into your container.

std::list<class_type*> l;
l.push_back(new class_type);

However though the objects now won't be copied, they also won't be automatically cleaned up for you when the list is destructed. Smart pointers will solve this for you, but at the cost of even more syntactic noise. And since you can't put std::auto_ptr's in standard containers because they can't be copied, you have to use their slightly heavier-weight boost cousins, shared pointers.

std::list<boost::shared_ptr<class_type> > l;
l.push_back(boost::shared_ptr<class_type>(new class_type));

Shared pointed do incur some extra overhead, but it is minimal.

Dave Branton
  • 494
  • 3
  • 11
5

Standard library containers require their types be copiable; as references are not, you can't store them in containers to begin with. You can store pointers, so long as you are careful about object lifetimes. Boost has some pointer containers to help with that, or else you have smart pointers. Note, however, that auto_ptr is not copiable (as the standard defines it for this purpose), so shared_ptr and unique_ptr are your best bets now. Both are standard in C++11, and the former is supported through boost in C++03.

Dennis Zickefoose
  • 10,791
  • 3
  • 29
  • 38
  • Actually, now that I think about it... given the way requirements are broken down by function in 11, if you limit yourself to a sufficiently small subset of operations, it *might* be possible to store a reference type, but the subset would be mighty limited. – Dennis Zickefoose Oct 10 '11 at 02:16
1

You want a container of pointers:

list<SampleClass*> l;
Mikhail
  • 7,749
  • 11
  • 62
  • 136
0

Now that you have smart pointer, you can use it do the memory management and take raw pointers from them to use in STL containers. In that way you keep the ownership out of the container.

Here I have a unique_ptr of a tree, but used STL stack to store the raw pointers

void TreeTraversal(unique_ptr<BinaryTreeNode>& root) {
    stack<BinaryTreeNode *> _stack;

    BinaryTreeNode *p = root.get();
    _stack.push(p);

    while(!_stack.empty()) {
        p = _stack.top();
        _stack.pop();
        ...
        _stack.push(p->left);
        _stack.push(p->right);
    }
}

int main() {
    unique_ptr<BinaryTreeNode> root = unique_ptr<BinaryTreeNode>(new BinaryTreeNode(...));
    TreeTraversal(root);
}
saha
  • 97
  • 8