136

When creating local variables, is it correct to use (const) auto& or auto?

e.g.:

SomeClass object;
const auto result = object.SomeMethod();

or const auto& result = object.SomeMethod();

Where SomeMethod() returns a non-primitive value - maybe another user-defined type. My understanding is that const auto& result is correct since the result returned by SomeMethod() would call the copy constructor for the returned type. Please correct me if I am wrong.

What about for primitive types? I assume const auto sum = 1 + 2; is correct.

Does this also apply to range based for loops?

for(const auto& object : objects)
rohunb
  • 1,443
  • 2
  • 11
  • 6
  • 1
    I highly recommend you to read this: https://www.safaribooksonline.com/library/view/effective-modern-c/9781491908419/ch01.html First two chapters are free and describe template type deduction, which is essentially how `auto` works (except for the peculiar case of `initializer_list`s, which are non-deduced in a template context), then `auto` type deduction. – vsoftco Apr 25 '15 at 01:18

4 Answers4

164

auto and auto && cover most of the cases:

  • Use auto when you need a local copy. This will never produce a reference. The copy (or move) constructor must exist, but it might not get called, due to the copy elision optimization.

  • Use auto && when you don't care if the object is local or not. Technically, this will always produce a reference, but if the initializer is a temporary (e.g., the function returns by value), it will behave essentially like your own local object.

    Also, auto && doesn't guarantee that the object will be modifiable, either. Given a const object or reference, it will deduce const. However, modifiability is often assumed, given the specific context.

auto & and auto const & are a little more specific:

  • auto & guarantees that you are sharing the variable with something else. It is always a reference and never to a temporary.

  • auto const & is like auto &&, but provides read-only access.

What about for primitive/non-primitive types?

There is no difference.

Does this also apply to range based for loops?

Yes. Applying the above principles,

  • Use auto && for the ability to modify and discard values of the sequence within the loop. (That is, unless the container provides a read-only view, such as std::initializer_list, in which case it will be effectively an auto const &.)
  • Use auto & to modify the values of the sequence in a meaningful way.
  • Use auto const & for read-only access.
  • Use auto to work with (modifiable) copies.

You also mention auto const with no reference. This works, but it's not very commonly used because there is seldom an advantage to read-only access to something that you already own.

Community
  • 1
  • 1
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • 1
    Sutter says that auto& tracks constness – Jesse Pepper Mar 14 '18 at 07:05
  • 1
    @JessePepper Yes. (It’s a bit odd to appeal to authority though.) Are you referring to a specific part of this? – Potatoswatter Mar 14 '18 at 07:09
  • 2
    In essence `auto&` seems to be a good choice. But using `const auto&` when you want to add constness that isn't already there. `auto&&` is for forwarding, which I do think happens more often than Sutter thinks. For example, you might want to store a return value by `auto&&` and then "forward" it to two things, like std::cout to see the value for debugging and also pass it to some other function. I used to use auto&& more often but have been bitten by it once or twice when it did some unexpected things. I wish I took more notice of what went wrong! – Jesse Pepper Mar 20 '18 at 01:55
  • @JessePepper Yes, `auto&&` is related of a missing language feature, that *should* allow any statement to be divided into smaller statements. The missing part is lifetime extension… I put quite a bit of effort into fixing this but nobody noticed. Don't put too much stock in punditry :) – Potatoswatter Mar 20 '18 at 10:45
  • On auto const: It may be more natural to write auto const x = fn(); if you know the function does not return a reference, and you want the object x not to change, to avoid bugs, or document it's use in the scope. In the same way you wouldn't normally write: const int& x = 1; But auto const & does give equivalent results due to lifetime extension rules for references declared in this way. – Spacen Jasset May 01 '20 at 15:27
77

Yes, it is correct to use auto and auto& for local variables. When getting the return type of a function, it is also correct to use auto&. This applies for range based for loops as well.

General rules for using auto are:

  • Choose auto x when you want to work with copies.
  • Choose auto &x when you want to work with original items and may modify them.
  • Choose auto const &x when you want to work with original items and will not modify them.

You can read more about the auto specifier here.

Darwyn
  • 4,696
  • 3
  • 25
  • 26
phantom
  • 3,292
  • 13
  • 21
  • thanks, but could you explain further? we have `auto x` to work with copies and use `auto const &x` without needing to modify. So what is the difference between these cases? – Karim Jun 19 '22 at 03:18
16

auto uses the same mechanism of type deduction as templates, the only exception that I am aware of being that of brace-init lists, which are deduced by auto as std::initializer_list, but non-deduced in a template context.

auto x = expression;

works by first stripping all reference and cv qualifiers from the type of the right hand side expression, then matching the type. For example, if you have const int& f(){...} then auto x = f(); deduces x as int, and not const int&.

The other form,

auto& x = expression

does not strip the cv-qualifiers, so, using the example above, auto& x = f() deduces x as const int&. The other combinations just add cv qualifiers.

If you want your type to be always deduced with cv-ref qualifiers, use the infamous decltype(auto) in C++14, which uses the decltype type deduction rules.

So, in a nutshell, if you want copies, use auto, if you want references, use auto&. Use const whenever you want additional const-ness.


EDIT There is an additional use case,

auto&& x = expression;

which uses the reference-collapsing rules, same as in the case of forwarding references in template code. If expression is a lvalue, then x is a lvalue reference with the cv-qualifiers of expression. If expression is a rvalue, then x is a rvalue reference.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • @Potatoswatter Thanks, edited. I actually wonder about the cv for `rvalues`. Is there any simple way of testing? And how can you have a cv-qualified rvalue? On function return cv is discarded. – vsoftco Apr 25 '15 at 03:48
  • 1
    No, it is not discarded on function returns. It is discarded for prvalues of all scalar types (C++14 [and other editions] §5/6). You can get one using a function call or cast notation. Cv-qualified xvalues work the same as anything else: `auto && x = std::move< const int >( 5 );` will declare `int const && x`, although they are seldom used. – Potatoswatter Apr 25 '15 at 06:02
  • @Potatoswatter thanks, you are right, my mistake. I tested first with PODs, in which case the cv is discarded, and thought that's the general case. Of course it shouldn't be discarded, since in general one may want to preserve the cv (e.g. so one cannot call non-const member function on rvalue returns). – vsoftco Apr 25 '15 at 12:48
  • Discarded for scalar types. PODs may be classes. Anyway, it's a hackish corner of the intersection of the expression model and the object model. – Potatoswatter Apr 25 '15 at 13:05
2

When creating local variables, is it correct to use (const) auto& or auto?

Yes. The auto is nothing more than a compiler-deduced type, so use references where you would normally use references, and local (automatic) copies where you would normally use local copies. Whether or not to use a reference is independent of type deduction.

Where SomeMethod() returns a non-primitive value - maybe another user-defined type. My understanding is that const auto& result is correct since the result returned by SomeMethod() would call the copy constructor for the returned type. Please correct me if I am wrong.

Legal? Yes, with the const. Best practice? Probably not, no. At least, not with C++11. Especially not, if the value returned from SomeMethod() is already a temporary. You'll want to learn about C++11 move semantics, copy elision, and return value optimization: https://juanchopanzacpp.wordpress.com/2014/05/11/want-speed-dont-always-pass-by-value/

http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=199

https://isocpp.org/wiki/faq/ctors#return-by-value-optimization

What about for primitive types? I assume const auto sum = 1 + 2; is correct.

Yes, this is fine.

Does this also apply to range based for loops?

for(const auto& object : objects)

Yes, this is also fine. I write this sort of code at work all the time.

Community
  • 1
  • 1
tweej
  • 832
  • 4
  • 16