What is the difference between auto and auto&?
Auto, for(auto x : range)
: This usage will create a copy of each element of the range.
Auto &, for(auto& x : range)
: when you want to modify the elements in range (without proxy class reference processing), use auto&.
ii
is a reference, so in the loop body, if ii
is modified, the corresponding element in a
will also be modified.
jj
is not a reference. In each loop, it is a copy of the corresponding element. Its modification will not affect the corresponding element in a
. Since each loop creates a copy, it will bring system overhead.
If you want to ensure that the data in a
is not modified and you want to improve the efficiency, you can use the form const auto & ii : a
.
C++ auto vs auto&
The type deduction for auto
works exactly the same as for templates:
- when you deduce
auto
you will get a value type. - when you deduce
auto&
you wil get a non-const reference type - when you deduce
const auto&
you will get a const reference - when you deduce
auto&&
you will get- a non-const reference if you assign a non-const reference
- a const reference if you assign a const reference
- a value when you assign a temporary
auto and auto& and const
Do I understand correctly that variables declared with auto (x1) will never be const
Correct.
When with auto&(x2) will be const if exp2 will be const.
A reference is never const; references cannot be cv qualified. x2
could be a reference to const.
auto it = find(cont.cbegin(), cont.cend(), value);
Here despite I use cbegin and cend it will be non-const iterator
it
would be a non-const qualified object of const_iterator type.
const auto it1 = find(cont.cbegin(), cont.cend(), value);
it1
would be a const qualified object of const_iterator type.
Indirecting through a const_iterator (typically) gives you a reference to const, and thus you cannot modify the pointed object.
A const object cannot (generally) be modified. Thus, you for example cannot increment a const qualified iterator.
Using auto vs const auto& to temporary keep result of method call
Is that correct?
Yes.
If so, should I prefer const auto& over auto for such cases all the time?
It depends on what you intend to do.
If you want a copy, then use an object variable. If you want to refer to an object stored elsewhere, then use a reference variable.
Also, should I prefer getCRef() interface over getValue() when I can guarantee that reference lives as long as underlying object does?
Same as above.
my intention is to pass unmodified result of one method call to another
let's assume that object doesn't change for the duration
If T
is trivial, then prefer object to avoid unnecessary indirection. If T
is non-trivial, then prefer reference to avoid copying. If you don't know whether T
is trivial, then assume that it is not.
Which one is better, auto&&, auto or auto const
It really depends on your needs and what you're iterating over.
Styles 1 and 3 make a copy of the element. This means you can't change the original container. For small types it may be beneficial in terms of performance (benchmark yourself on your target architecture), but usually is just a bad default.
You then have to decide whether you're one of the const by default folks. If yes, take style 3.
Style 2 is indeed most generic, but not necessarily in the good way. Unless you want to further move from the element, there's little point to taking a non-const reference to a sequence generated on the spot, i.e.:
for(auto&& x : std::vector<int>{1,2,3})
{
// x = 42; ???
}
If you look at C++ Core Guidelines here and here, you'll see no examples with auto&&
and they suggest taking const&
by default unless you're going to modify the variable. That's what I'd stick to.
Difference between const auto & and auto & if object of reference is const
auto
keyword automatically decides the type of the variable at compile time.
In your first case, auto
is reduced to int
where it's reduced to const int
in the second case. So, both of your cases are reduced to the same code as:
const int &k = i;
However, it's better to have the const explicitly for better readability and to make sure your variable TRULY is const
.
Difference between capturing return value by `const auto&` and `auto`
In the shown example, there is no reason to use a reference. The behavior of the snippet is identical with or without it.
In general, it's a good idea to use a reference to avoid incurring needless copies, so there's no harm in getting into the habit of using references, even if it doesn't make a difference in some cases.
I'd suggest writing const
on the east though. This is just a preference, and definitely doesn't change the meaning of the code, but it's more consistent. I would write it as:
auto const & now = time(nullptr);
What does auto&& tell us?
By using auto&& var = <initializer>
you are saying: I will accept any initializer regardless of whether it is an lvalue or rvalue expression and I will preserve its constness. This is typically used for forwarding (usually with T&&
). The reason this works is because a "universal reference", auto&&
or T&&
, will bind to anything.
You might say, well why not just use a const auto&
because that will also bind to anything? The problem with using a const
reference is that it's const
! You won't be able to later bind it to any non-const references or invoke any member functions that are not marked const
.
As an example, imagine that you want to get a std::vector
, take an iterator to its first element and modify the value pointed to by that iterator in some way:
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
This code will compile just fine regardless of the initializer expression. The alternatives to auto&&
fail in the following ways:
auto => will copy the vector, but we wanted a reference
auto& => will only bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues
So for this, auto&&
works perfectly! An example of using auto&&
like this is in a range-based for
loop. See my other question for more details.
If you then use std::forward
on your auto&&
reference to preserve the fact that it was originally either an lvalue or an rvalue, your code says: Now that I've got your object from either an lvalue or rvalue expression, I want to preserve whichever valueness it originally had so I can use it most efficiently - this might invalidate it. As in:
auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));
This allows use_it_elsewhere
to rip its guts out for the sake of performance (avoiding copies) when the original initializer was a modifiable rvalue.
What does this mean as to whether we can or when we can steal resources from var
? Well since the auto&&
will bind to anything, we cannot possibly try to rip out var
s guts ourselves - it may very well be an lvalue or even const. We can however std::forward
it to other functions that may totally ravage its insides. As soon as we do this, we should consider var
to be in an invalid state.
Now let's apply this to the case of auto&& var = foo();
, as given in your question, where foo returns a T
by value. In this case we know for sure that the type of var
will be deduced as T&&
. Since we know for certain that it's an rvalue, we don't need std::forward
's permission to steal its resources. In this specific case, knowing that foo
returns by value, the reader should just read it as: I'm taking an rvalue reference to the temporary returned from foo
, so I can happily move from it.
As an addendum, I think it's worth mentioning when an expression like some_expression_that_may_be_rvalue_or_lvalue
might turn up, other than a "well your code might change" situation. So here's a contrived example:
std::vector<int> global_vec{1, 2, 3, 4};
template <typename T>
T get_vector()
{
return global_vec;
}
template <typename T>
void foo()
{
auto&& vec = get_vector<T>();
auto i = std::begin(vec);
(*i)++;
std::cout << vec[0] << std::endl;
}
Here, get_vector<T>()
is that lovely expression that could be either an lvalue or rvalue depending on the generic type T
. We essentially change the return type of get_vector
through the template parameter of foo
.
When we call foo<std::vector<int>>
, get_vector
will return global_vec
by value, which gives an rvalue expression. Alternatively, when we call foo<std::vector<int>&>
, get_vector
will return global_vec
by reference, resulting in an lvalue expression.
If we do:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
We get the following output, as expected:
2
1
2
2
If you were to change the auto&&
in the code to any of auto
, auto&
, const auto&
, or const auto&&
then we won't get the result we want.
An alternative way to change program logic based on whether your auto&&
reference is initialised with an lvalue or rvalue expression is to use type traits:
if (std::is_lvalue_reference<decltype(var)>::value) {
// var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
// var was initialised with an rvalue expression
}
Related Topics
Why Is the C++ Stl Is So Heavily Based on Templates? (And Not on *Interfaces*)
Convert a Char* to Std::String
Iterate Through a C++ Vector Using a 'For' Loop
Using 3Rd Party Header Files with Rcpp
How to Make Objective-C Class Using Std:Vector Made Available to Swift Classes
How to Copy the Contents of an Array to a Std::Vector in C++ Without Looping
Benefits of Header-Only Libraries
What Is the Easiest Way to Make a C++ Program Crash
Class & Function Names Highlighting in Vim
Equivalent C++ to Python Generator Pattern
How to Copy Contents of a Directory into Build Directory After Make with Cmake
Detailed Guide on Using Gcov with Cmake/Cdash
Thread Safe Implementation of Circular Buffer
Is the Ranged Based for Loop Beneficial to Performance
Is There Still a Use for Inline
How to Directly Bind a Member Function to an Std::Function in Visual Studio 11