0

im pretty new to C++, learning by doing right now. Within the class material, I have the following function:

std::vector<std::complex<double>> material::get_idx(const std::vector<double>& numbers) {

  std::vector<std::complex<double>> res;

  for (auto& num : numbers) {
    // calculate with num (we don't modify it) and push_back to res 
    ...
  }

  return res;
}

A couple of minutes ago I used the normal for loop like this: for(size_t i=0; i<numbers.size(); i++). Since I've read that using ranged-based for loop with auto can be more efficient than plain for loops, I changed it. Now I'm asking myself if it's correct/senseful/good practice to write the ranged-based for loop with a reference like this: for(auto& num : numbers) and what's the difference to using for(auto num : numbers). I'm kind of confused because I'm already handing over the numbers vector per reference in the signature of the get_idx function. Of course I want to hand over the vector numbers to the function get_idx per reference, but how about looping over those elements via reference?

Brosch
  • 98
  • 8
  • 3
    My advice, `const auto&` if you don't want to modify, `auto&&` if you do. – NathanOliver Mar 11 '21 at 13:20
  • Hey @NathanOliver, thanks for your answer, what is the difference of using const auto& instead of just const auto? – Brosch Mar 11 '21 at 13:21
  • The first is a reference, so no copy. The second is a value so a copy is made. Copying can be expensive. – NathanOliver Mar 11 '21 at 13:22
  • Ok so in the function signature we make sure to hand over the vector per reference and in the for loop we use syntax to access the vector elements per reference and don't copy each element to then process it. Is that right? – Brosch Mar 11 '21 at 13:25
  • `for (auto num: numbers)` means that `num` is the value of elements of `numbers`, not a reference. `for (auto & num : numbers)` means that `num` will, in each loop iteration, be a reference to an element of `numbers` - so changing `num` will change the corresponding element (assuming `numbers` itself isn't `const`). `for (const auto & num : numbers)` means that `num` is a reference to an element (not a copy) but it cannot be changed - which is useful if copying elements of `numbers` (i.e. obtaining their value) is expensive. – Peter Mar 11 '21 at 13:25
  • Ok, thanks, my initial thought was that I don't have to use the reference in the for loop again since the vector is already passed by reference in the function signature. So it is generally good practice to use it like this: `for(const auto& num)` if we don't want to change the elements in numbers within the loop, even those are just integers? – Brosch Mar 11 '21 at 13:29
  • See https://stackoverflow.com/questions/15176104 for a detailed discussion of that. – cigien Mar 11 '21 at 13:32
  • "Since I've read that using ranged-based for loop with auto can be more efficient than plain for loops" this sounds rather odd. There is no reason to believe that a range based for loop is faster or slower than its equivalent iterator based loop – 463035818_is_not_an_ai Mar 11 '21 at 13:33
  • Sorry, I've expressed myself incorrectly. I meant the use of auto can be more efficient than using the plain (supposed) data type within the loop, see https://stackoverflow.com/questions/32510183/can-the-use-of-c11s-auto-improve-performance/32510343#32510343 – Brosch Mar 11 '21 at 13:42
  • Does it make a difference to use `auto const& num` (as it is used in https://stackoverflow.com/questions/15176104 @largest_prime_is_463035818) instead of `const auto& num` – Brosch Mar 11 '21 at 13:44
  • @largest_prime_is_463035818: for-range ensures `std::end(container)` is called once, whereas traditional iterator loop will call it at each iteration. Impact should be really minor though. – Jarod42 Mar 11 '21 at 13:44
  • the answer you refer to talks about implicit conversion due to picking the wrong type. If you replace `auto` with the exact type that `auto` gets deduced as, then there is literally zero difference – 463035818_is_not_an_ai Mar 11 '21 at 13:45
  • For integers and floating point values, I'd rather write the read-only case without a reference: `(const auto num : numbers)` – Secundi Mar 11 '21 at 13:46
  • @Jarod42 point taken, though it is possible to write an (non-traditional) iterator loop that is equivalent to the range based one – 463035818_is_not_an_ai Mar 11 '21 at 13:46
  • @Brosch: `const auto&` and `auto const&` are same. (as `int* p` versus `int *p`). stylistic difference. – Jarod42 Mar 11 '21 at 13:46
  • Yes, that's why I wrote "can" be more efficient, if there are some really complex objects/types where it's not that obvious which type to take. – Brosch Mar 11 '21 at 13:47
  • @Jarod42 thanks! and thanks to all of you, I understood it now. So much more to learn, what a great language. – Brosch Mar 11 '21 at 13:48
  • 1
    you never run out of new things to learn in C++, though i am not always sure if thats a good thing ;) – 463035818_is_not_an_ai Mar 11 '21 at 13:49
  • 1
    Well at least it's a good thing for sure to have people here being able to explain things voluntarily. Thanks again, have a great day folks! – Brosch Mar 11 '21 at 13:51

1 Answers1

1

auto makes a copy, auto& makes an lvalue reference of any kind but will not bind to rvalues, auto const& makes an lvalue reference to const that can bind to rvalues and induce reference lifetime extension, auto&& generates a forwarding reference that can bind to anything and can induce reference lifetime extension. auto const makes a copy and makes it immutable.

Which is more correct depends on which you want.

You could just type double const&, double const or double instead of anything to do with auto.

I start with auto&&, which is shorthand for "I do not care, just make it work". Next I'd use auto, as values make reasoning easier. And if I had a worry about making it clear I am not editing, auto const or auto const&.

If I wanted to edit the elements inplace, auto&.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Its `for(double const& num : numbers)` rather than `for(std::complex const& num : numbers)` – Brosch Mar 12 '21 at 14:11