Consider the following:
std::vector<int> things;
// fill things here
const auto i = static_cast<int>(things.size()) - 1;
// "VectorType &&" resolves to "std::vector<int> &&" -- forwarded indirectly
recurse(move(things), i);
// "VectorType &&" resolves to "std::vector<int> &" -- also forwarded indirectly
recurse(things, i);
// "VectorType &&" resolves to "const std::vector<int> &" -- again, forwarded indirectly
recurse(static_cast<const std::vector<int> &>(things), i);
Even after all 3 calls to recurse
in the example above, the things
vector would still be intact.
If the recursion were changed to:
return vec[i] + recurse(forward<VectorType>(vec), --i);
the results would then be undefined, as either vec[i]
or --i
could be evaluated in either order.
Function calls are like sequence points: The results of argument expressions must be computed before the function is called. The order in which this happens, however, is undefined -- even with respect to sub-expressions within the same statement.
Forcing the construction of an intermediate within the recursion statement would also result in undefined behavior.
For example:
template<typename VectorType>
auto recurse(VectorType &&vec, int i)
{
using ActualType = typename std::decay<VectorType>::type;
if(i < 0){
return decltype(vec[i]){};
}
return vec[i] + recurse(ActualType{forward<VectorType>(vec)}, i - 1);
}
Here, vec[i]
or ActualType{forward<VectorType>(vec)}
could be evaluated in either order. The latter would either copy-construct or move-construct a new instance of ActualType
, which is why this is undefined.
In Summary
Yes, your example will sum the contents of the vector.
There is no reason for the compiler to construct an intermediate, so successive invocations of recurse
each receive an indirect reference to the same, unchanging instance.
Addendum
As pointed out in a comment, it is possible for a non-const operator[]
to mutate the instance of VectorType
, whatever that might be. In this case, the result of the recursion would be undefined.