What is the difference between 1 and 2? When what to use?

for (auto i : container){} // 1 for (auto&& i : container){} // 2 

4 answers 4

When you have an ad type

 auto &&x = initializer; 

then the given rvalue reference &&x is special and is called forwarding reference . If the initializer is an lvalue , then this reference takes the lvalue type of the reference.

From the C ++ standard (Document Number: N4296, 7.1.6.4 auto speci er)

  1. ... Deduce a value for a function call (14.8.2.1) , where a direct call -initialization.

And further (14.8.2.1 Deducing template arguments from a function cal)

  1. ... A forward reference reference parameter a cv-unqualified template parameter. If you’re referring to the lion, you’re

What does this mean? This means that the following two declarations will be equivalent.

 int x = 0; auto &&r = x; 

and

 int x = 0; auto &r = x; 

Below is a demo program.

 #include <iostream> int main() { { int x = 0; std::cout << "x = " << x << std::endl; auto &&r = x; r = 10; std::cout << "x = " << x << std::endl; } { int x = 0; std::cout << "x = " << x << std::endl; auto &r = x; r = 10; std::cout << "x = " << x << std::endl; } } 

Its output to the console

 x = 0 x = 10 x = 0 x = 10 

It also follows that when you deal with a for clause based on a range, and the corresponding iterator returned by the begin function, or the corresponding pointer, after applying the dereference operator returns the lvalue value of the original object (pointers always return the lvalue value after dereference), then you You can change this source object using the forwarding reference declaration.

For example,

 #include <iostream> #include <vector> int main() { { int a[] = { 0 }; std::cout << "a[0] = " << a[0] << std::endl; auto &&r = *a; r = 10; std::cout << "a[0] = " << a[0] << std::endl; } { std::vector<int> v(1); std::cout << "v[0] = " << v[0] << std::endl; auto &&r = *v.begin(); r = 10; std::cout << "v[0] = " << v[0] << std::endl; } } 

The output of the program to the console:

 a[0] = 0 a[0] = 10 v[0] = 0 v[0] = 10 

Therefore, for example, for standard containers that return a reference to the original object from a dereferenced iterator, two similar for declarations are equivalent

 std::vector<int> v { 1, 2, 3, 4, 5 }; for ( auto &&x : v ) x *= 2; 

and

 std::vector<int> v { 1, 2, 3, 4, 5 }; for ( auto &x : v ) x *= 2; 

Since in both cases there is an inferred type of the variable x as int & .

When is a view declaration used

 auto x = initializer; 

then the type of variable x is inferred from the initializer type specifiers, ignoring references. That is, even if the initializer is a reference type, such as int & , the type of the variable x is int , and therefore you cannot change the original object using this variable. For example,

 #include <iostream> #include <vector> int main() { { std::vector<int> v{ 1, 2, 3, 4, 5 }; for (int x : v) std::cout << x << ' '; std::cout << std::endl; for (auto &&x : v) x *= 2; for (int x : v) std::cout << x << ' '; std::cout << std::endl; } std::cout << std::endl; { std::vector<int> v{ 1, 2, 3, 4, 5 }; for (int x : v) std::cout << x << ' '; std::cout << std::endl; for (auto x : v) x *= 2; for (int x : v) std::cout << x << ' '; std::cout << std::endl; } } 

Compare the output of two code blocks of this program.

 1 2 3 4 5 2 4 6 8 10 1 2 3 4 5 1 2 3 4 5 

If the forwarding reference initialized by the rvalue value, and not the lvalue value, then the rvalue link will not turn into a lvalue link. This difference is shown below.

 #include <iostream> int f() { static int x; return x; } int & g() { static int x; return x; } int main() { { auto &&x = f(); std::cout << "x = " << x << std::endl; x = 10; std::cout << "x = " << x << std::endl; std::cout << "f() = " << f() << std::endl; } std::cout << std::endl; { auto &&x = g(); std::cout << "x = " << x << std::endl; x = 10; std::cout << "x = " << x << std::endl; std::cout << "g() = " << g() << std::endl; } } 

Output of the program to the console

 x = 0 x = 10 f() = 0 x = 0 x = 10 g() = 10 

As for the question

When what to use?

it is better to use auto & if you want to change the values ​​in the loop that the link will refer to, or const auto & when you use read-only values. auto makes sense to use for fundamental types, for example, arithmetic types or pointers), when copying an object into a local variable to be created is not a resource-expensive operation and you do not need to change the original objects.

  • @VladD Thank you for noticing. - Vlad from Moscow
  • Something I do not see is the term "forwarding reference" in the Standard. - αλεχολυτ
  • @alexolut I specified which working draft of the standard I refer to in my answer. - Vlad from Moscow

It is necessary to choose, depending on the context, about which you did not mention at all. For example, the following code:

 #include <vector> int main() { std::vector<bool> v; for (auto i: v) { } for (auto& i: v) { } // ошибка for (auto&& i: v) { } } 

will give an error for auto& , because vector<bool> uses an auxiliary reference class, the rvalue object of which cannot be associated with a nonconstant reference. Because of the proxy class, another interesting situation arises, that modification i in the auto and auto&& cycles will behave the same, i.e. change the value stored in the container.

If instead of std::vector<bool> use std::vector<int> , then auto will only allow you to change the local variable , and auto&& (like auto& ) already has the value in the container.

  • 2
    Here are many subtleties to keep in mind to write reliable programs in C ++. - avp
  • What other types, besides bool, do they use a proxy class in the vector? - Majestio
  • one
    @StanislavPetrov if approximately, then one bit is used for each element. - αλεχολυτ
  • one
    @StanislavPetrov The minimum unit of addressing in C ++ is a byte. Therefore, you need to fence all sorts of proxy types in order to try to refer to a specific bit. - αλεχολυτ
  • one
    @StanislavPetrov is obtained because a temporary object is returned. Here is the code to understand the point. - αλεχολυτ

In the first case, the search is by value, i.e. You cannot change items.

In the second case - on the universal link. those. in the second case, you can write a code like:

 for (auto&& i : container){ ++i;} 
  • in the first case, you can also write like this - user230909
  • @ user230909 just what's the point in (1) so to write? A good compiler, by the way, should give an error. - pavel
  • @pavel on what basis an error? - user230909
  • You can write in the first :), only to no purpose. And in the second it is enough for(auto& i... - so why two && ? :) - Harry
  • one
    Two && are needed in order to work with both lvalue and rvalue links. - vi.ki.ng

There is no difference. The universal reference in the for loop is used so that the code is compatible with containers whose iterator returns the rvalue of the reference.

 template<typename C> void interate1(C& container) //Non-const { for(auto& v: container) { //Do something with v } } template<typename C> void iterate2(C& conatiner) { for(auto&& v: container) { //Do soemthign with v } } ... std::vector<bool> v(10); iterate1(v); //Error iterate2(v); //OK