Let's suppose I have a vector of integers and want to process it in an odd recursive manner (this situation might sound strange without a context, but still).
I want to use const_iterators to track current position. Here is odd_recursive_stuff()
prototype:
// Note: changing "std::vector<int>::const_iterator& it_cur"
// into "std::vector<int>::const_iterator it_cur" will change
// the side effects!
void odd_recursive_stuff (std::vector<int>::const_iterator& it_cur,
std::vector<int>::const_iterator it_end);
Firstly I tried to call it this way:
void process_vec (const std::vector<int> &vec) {
odd_recursive_stuff (std::begin(vec), std::end(vec));
}
Luckily, It doesn't compile (in clang 8.0.0 for example):
Error: no matching function for call to 'recursive_odd_stuff'
Candidate function not viable: expects an l-value for 1st argument!
Because std::begin()
returns r-value, so I have to call it another way that works:
void process_vec (const std::vector<int> &vec) {
std::vector<int>::const_iterator it_beg = std::begin (vec);
recursive_odd_stuff (it_beg, std::end(vec));
}
Now I'm wondering if it is possible to call the base of recursive_odd_stuff()
with a single line without local_variable it_beg
?
It seems that It is impossible to write another version of begin()
which returns l-value, because "the return value of a function is an l-value if and only if it is a reference (C++03). (5.2.2 [expr.call] / 10)". So the only way is to call it with two lines?
So, there's a way to make it a one-liner, but I don't recommend it:
#include <vector>
#include <functional>
void odd_recursive_stuff (std::vector<int>::const_iterator& it_cur,
std::vector<int>::const_iterator it_end){}
void process_vec (const std::vector<int> &vec) {
odd_recursive_stuff ([it=std::begin(vec)]()mutable{return std::ref(it);}(), std::end(vec));
}
I think that your n-th recursive call changes the reference which is then used by the n-1th caller to do something. In this case, I would recommend splitting the code into two functions:
odd_recursive_stuff(IT begin, IT end){
odd_recursive_stuff_impl(begin, end);
}
odd_recursive_stuff_impl(IT& begin, IT& end){
....
}
This exposes a public interface that just requires iterators. Later when you change the algorithm in a way that does not require the reference, or it will require end
to be a reference too, then you don't have to change all calls.
The first solution might expand into something akin to this:
void process_vec (const std::vector<int> &vec) {
using IT = std::vector<int>::const_iterator;
struct _lambda_type{
_lambda_type(const IT& it):_it(it){}
//By default lambda's () is const method, hence the mutable qualifier.
std::reference_wrapper<IT> operator()()/*Not const*/{
return std::ref(_it);
}
private:
IT _it;
};
//Previous lines...
{//The line with the call.
//Lambda is created before the call and lives until the expression is fully evaluated.
_lambda_type lambda{std::begin(vec)};
odd_recursive_stuff (lambda(), std::end(vec));
}//Here's the lambda destroyed. So the call is perfectly safe.
//The rest...
}
The lambda's operator()
returns a reference to a local variable, but it's local to the lambda object, not the operator()
itself. Because the lambda object lives until the end of the expression(;
) the call is safe. Just note that I used std::ref
as a quick way to return a reference without the need to mention the return type explicitly. std::reference_wrapper<T>
is then implicitly convertible to T&
.
return it;
would return by value and [it=std::begin(vec)]()mutable ->decltype(it)&{...};
is not possible either. ->decltype(std::begin(vec))&{
works but it's wordy. Another alternatives are to write the iterator's type explicitly or use a using
but that's even worse.