0

I have found in a C++ design pattern tutorial the following code:

  vector<Object*> objects;
  void foo() override
  {
    cout << "Group contains:\n";
    for (auto&& o : objects)
      o->foo();
  }

What is the purpose to use right-reference in the loop? I know it is used to avoid copy of temporary objects and implement move semantics, but I cannot see how it would apply in this case and why not using a simple

for (auto o : objects)
  o->foo();

In addition what would have happen if using auto& o? All these possibilities compile correctly and produce the correct results...

Vadim Kotov
  • 7,103
  • 8
  • 44
  • 57
giuseppe
  • 150
  • 1
  • 11

2 Answers2

3

for (auto o : objects) will create mentioned copies, as it uses value semantics to get objects. You could use auto& o to avoid this.

However, this won't work if dereferencing the iterator (which is done automatically by the range for) returns some proxy object (in general: returns by value). An example may be std::vector<bool> which uses such "proxy reference".

Then, this will not work:

std::vector<bool> vec;
for (auto& v : vec) // fail
{
  ...
}

because you cannot bind rvalue reference to non-const lvalue reference. Thus, you can use universal reference to fix this:

std::vector<bool> vec;
for (auto&& v : vec) // ok
{
  ...
}

In the example you posted, vector holds pointers to objects, so there is no practical difference - if you do not want to modify the content (and thus, do not need to fetch the pointers by reference), using value is perfectly fine, as copying pointer only causes its value to be copied, not the pointee. So there is no real gain. Using rvalue reference has also no advantage, except it correctly propagates object const-ness and is more bullet-proof if you change the type of the elements vector stores (it will still give you proper type, no matter if *iterator returns const/non-const lvalue/rvalue reference).


Update: Actually, I have just found similar question, that was answered with almost the same example as I provided. Link.

Mateusz Grzejek
  • 10,635
  • 3
  • 30
  • 47
  • 1
    So, why `auto&&` is used with `vector objects;`? – Yola Feb 14 '18 at 12:16
  • 1
    This doesn't answer the question. Since the vector is a vector of pointers then taking copies looks fine to me. – freakish Feb 14 '18 at 12:19
  • @freakish For pointers, using rv ref gives no advantage, except the code is more bullet-proof and will correctly adjust itself to the object's type, if changed. Updated. I'm really curious what was worth the downvote. – Mateusz Grzejek Feb 14 '18 at 12:55
  • Still not that clear in which case it will be useful and if it has a real advantage when using pointer... But I suppose I do not have to care too much about it :) – giuseppe Feb 14 '18 at 13:30
  • 1
    @giuseppe IMHO it is better to explicitly specify how do you reference objects you iterate through. Using const/non-const or ref/value depends on what kind of access do you want to get. Using rvalue references only makes sense in case of temporary objects, like in the example I provided. Otherwise it is unclear what was the intention, since there is no technical justification to do so. – Mateusz Grzejek Feb 14 '18 at 13:39
  • It is an example of the Composition design pattern. The class diagram has a Shared interface (Object in the snippet I wrote) a class "SingleObject" and a class "MultiObject" the last one contains the vector objects. And both override the foo function of the Object interface. – giuseppe Feb 14 '18 at 14:21
2

What is the purpose to use right-reference in the loop?

Using auto&& as an universal reference in a range-based loop has several advantages:

  • const propagation to the iterated elements

  • Better maintainability: If you ever change the elements the container holds, it will do the right thing. If you were to change to a std::vector<Object> it would still get a reference instead of a copy (if you were using for(auto o : objects)

There has been said a lot about auto and where to use it. I specially like this talk from Herb Sutter in which he advocates for the use of it and gives some pretty good guidance.

amc176
  • 1,474
  • 6
  • 18
  • Yes, maintanance is a valid argument. Cause I think in the particular case `auto o` is fine. But that plays badly with changes. – freakish Feb 14 '18 at 12:24
  • What do you mean with "Better maintanability"? If I change from std::vector to std::vector then I need to change my code from using o.foo() to o->foo()... – giuseppe Feb 14 '18 at 12:53
  • @giuseppe still you need to change only `->` to `.` and you don't bother about the `auto` – Yola Feb 14 '18 at 12:59