Click here to Skip to main content
15,887,135 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
I've tried to look up an explanation of how to correctly 'auto'-define an item type in a ranged for loop header (see code below), but I keep stumbling over slightly differnt uses, while failing to find an explanation.

Can someone please explain - or point me to a source explaining - what the exact meaning of a forwarding reference (auto&&) is compared to a normal reference (auto&), and when you should use which?

Related: since references can only be const, should I specify a const qualifier, or does auto include that? While I have an idea about that, I failed to find a good source explaining this, too, and I'd like to hear a second opinion. ;-)

What I have tried:

C++
#include <vector>
template <class T>
void do_something(const std::vector<T> my_container) {
    for (auto&& item_rr : container) {
        // do something with item
    }
    for (auto& item_r : container) {
        // do something with item
    }
}
Posted
Updated 11-Jan-21 5:51am
v2

Read the c++ aut documentation for details.

Looks like the first auto statement has a & symbol too much.

tip: read error messages careful and identify the claiming code line and its cause. Google for the error number and post the original and complete line in Q&A.
 
Share this answer
 
Comments
Stefan_Lang 11-Jan-21 7:34am    
As for reading error messages: the problem is there is no error, not even a warning: it appears to be valid code. I just wasn't sure what it means and whether omitting one '&' would maintain or alter the semantics. And I couldn't find a good handle on what to search for.

Thanks for the link. It does mention the use of auto&& in range-based for:

"For example, given const auto& i = expr;, the type of i is exactly the type of the argument u in an imaginary template template<class u=""> void f(const U& u) if the function call f(expr) was compiled. Therefore, auto&& may be deduced either as an lvalue reference or rvalue reference according to the initializer, which is used in range-based for loop."

I'm still trying to wrap my head around that, but at least that's something to work with.
KarstenK 11-Jan-21 10:14am    
The code may be valid, but the types of both loops are different. This isnt an issue, because no code is in them. So write some code into the loop and you will see that the & makes a difference.
It has to do with lvalues and rvalues. This is a really good, easy to understand introduction to them in my opinion - Understanding lvalues and rvalues in C and C++[^]. Another good one is this - Rvalue references[^]. It's a little denser but it goes into more details (e.g. reference collapsing, move semantics).

Lastly, I'm not 100% but I wanna say when using auto with a reference or pointer it retains the cv-qualifier.
 
Share this answer
 
v2
Comments
Stefan_Lang 11-Jan-21 8:04am    
I know what rvalue references are. Well, mostly. I was just confused on what auto makes out of this. It doesn't help that in the actual code the container elements are supposed to be constant - I suspect that in this case it really doesn't make sense, but I wanted to be sure.

But I guess it won't hurt checking on "rr" again. Thanks for the links.
I started investigating the answer to your question thinking I understand references. It turned out they are more complicated than I thought.

I wanted to say that, looking at your code, it depends what you want to do with the item_r and item_rr. Being a rvalue reference, I would have expected that you cannot say something like:
C++
template <class T>
T do_something_rval (std::vector<T>& container) {
  T t = 0;
  for (auto&& item_rr : container) {
    // do something with item
    t += item_rr;
    item_rr = t; // Expected an error here
  }
  return t;
}
However I didn't get any error neither on definition nor on instantiation (my compiler is MSVC++ 19 updated with latest patches):
C++
int main ()
{
  vector<int> v1 = { 1, 2, 3, 4, 5 };
  int sum_rval = do_something_rval (v1);
  cout << "v1";
  for (auto i : v1)
    cout << " " << i;
  cout << " sum=" << sum_rval << endl;

And the output:
v1 1 3 6 10 15 sum=15
shows that you can modify an object through a rvalue reference.

If, however, I change the 'auto' to an explicit type:
C++
template <class T>
T do_something_rval (std::vector<T>& container) {
  T t = 0;
  for (int&& item_rr : container) {
    // do something with item
    t += item_rr;
    item_rr = t;
  }
  return t;
}
I get an error:
Error C2440 'initializing': cannot convert from 'int' to 'int &&'

It goes to show that for auto declarations the compiler drops any rvalue reference specifications.

My conclusion: there is no point in using rvalue reference for auto declarations.
 
Share this answer
 
Comments
Stefan_Lang 14-Jan-21 2:57am    
Thanks for taking the time to investigate. Your results confirm what I suspected: that in case of doubt you better test what, exactly that rvalue ref does or doesn't do.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900