如何正确地将const_iterator&传递给函数?

时间:2019-06-23 19:36:24

标签: c++ const-iterator

让我们假设我有一个整数向量,并想以一种奇怪的递归方式处理它(这种情况在没有上下文的情况下听起来很奇怪,但仍然如此)。

我想使用const_iterators跟踪当前位置。这是odd_recursive_stuff()原型:

// 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);

首先,我尝试这样称呼它:

void process_vec (const std::vector<int> &vec) {
  odd_recursive_stuff (std::begin(vec), std::end(vec));
}

幸运的是,它无法编译(例如在clang 8.0.0中):

Error: no matching function for call to 'recursive_odd_stuff'
Candidate function not viable: expects an l-value for 1st argument!

因为std::begin()返回r值,所以我不得不用另一种可行的方式称呼它:

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));
}

现在我想知道是否可以用不带local_variable recursive_odd_stuff() 的一行来调用it_beg的基数?

似乎不可能写出另一个返回l值的begin()版本,因为“当且仅当它是引用时,函数的返回值才是l值(C ++ 03)。(5.2.2 [expr.call] / 10)”。所以唯一的方法就是用两行来调用它?

2 个答案:

答案 0 :(得分:2)

因此,有一种方法可以使其成为单线,但我不建议这样做:

#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));
}

我认为您的第n个递归调用会更改引用,然后该引用将被第n-1个调用者用来执行某些操作。在这种情况下,我建议将代码分成两个功能:

odd_recursive_stuff(IT begin, IT end){
    odd_recursive_stuff_impl(begin, end);
}
odd_recursive_stuff_impl(IT& begin, IT& end){
    ....
}

这公开了仅需要迭代器的公共接口。稍后,当您以不需要引用的方式更改算法,或者也需要end作为引用时,则不必更改所有调用。

第一个解决方案可能会扩展为与此类似的内容:

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...
}

lambda的operator()返回对局部变量的引用,但它对lambda对象是局部的,而不是operator()本身。因为lambda对象一直存在到表达式(;)的末尾,所以调用是安全的。请注意,我使用std::ref作为返回引用的快速方法,而无需显式提及返回类型。然后std::reference_wrapper<T>可以隐式转换为T&

return it;将按值返回,而[it=std::begin(vec)]()mutable ->decltype(it)&{...};也不可能。 ->decltype(std::begin(vec))&{有用,但是罗word。另一种选择是显式地编写迭代器的类型或使用using,但这甚至更糟。

答案 1 :(得分:2)

超载!

具有仅接受右值的版本:

void odd_recursive_stuff (std::vector<int>::const_iterator&& it_cur, 
                          std::vector<int>::const_iterator   it_end);

…以及接受左值引用(并为您做多余的一行)的版本:

void odd_recursive_stuff (const std::vector<int>::const_iterator& it_cur, 
                                std::vector<int>::const_iterator  it_end)
{
    std::vector<int>::const_iterator it_copy(it_cur);
    odd_recursive_stuff(std::move(it_copy), it_end);
}

这与移动语义所基于的原理相同,因为复制和移动构造函数的选择方式相同。

但是您可以考虑删除整个内容,而只返回it的新值:

std::vector<int>::const_iterator
odd_recursive_stuff(std::vector<int>::const_iterator it_cur, 
                    std::vector<int>::const_iterator it_end);

然后您可以随意丢弃它。

没人真正希望迭代器被引用。