如何正确地将 const_iterator& 传递给函数?
How to properly pass const_iterator& into a function?
假设我有一个整数向量并想以一种奇怪的递归方式处理它(如果没有上下文,这种情况可能听起来很奇怪,但仍然如此)。
我想使用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()
returns 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 it_beg
的情况下用单行调用recursive_odd_stuff()
的基数?
似乎不可能编写 begin()
的 returns 左值的另一个版本,因为 "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)"。那么唯一的办法就是用两行来调用它?
所以,有一种方法可以使它成为 one-liner,但我不推荐它:
#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-th 递归调用改变了引用,然后第 n-1 个调用者使用它来做某事。在这种情况下,我建议将代码拆分为两个函数:
odd_recursive_stuff(IT begin, IT end){
odd_recursive_stuff_impl(begin, end);
}
odd_recursive_stuff_impl(IT& begin, IT& end){
....
}
这公开了一个只需要迭代器的 public 接口。稍后当您以不需要参考的方式更改算法时,或者它也需要 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()
return 是对局部变量的引用,但它是 lambda 对象的局部变量,而不是 operator()
本身。因为 lambda 对象一直存在到表达式 (;
) 结束,所以调用是安全的。请注意,我使用 std::ref
作为 return 引用的快速方法,而无需明确提及 return 类型。 std::reference_wrapper<T>
然后隐式转换为 T&
.
return it;
按值会 return 而 [it=std::begin(vec)]()mutable ->decltype(it)&{...};
也不可能。 ->decltype(std::begin(vec))&{
有效,但很冗长。另一种选择是显式编写迭代器的类型或使用 using
,但那更糟。
超载!
有一个只接受右值的版本:
void odd_recursive_stuff (std::vector<int>::const_iterator&& it_cur,
std::vector<int>::const_iterator it_end);
…和一个接受 lvalue-references 的版本(并为你做额外的行):
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);
}
这与移动语义所基于的原则相同,因为复制和 move-constructors 的选择方式相同。
但您可能会考虑放弃这一切,只返回 it
的新值:
std::vector<int>::const_iterator
odd_recursive_stuff(std::vector<int>::const_iterator it_cur,
std::vector<int>::const_iterator it_end);
然后您可以随意丢弃它。
没有人真的希望迭代器被引用。
假设我有一个整数向量并想以一种奇怪的递归方式处理它(如果没有上下文,这种情况可能听起来很奇怪,但仍然如此)。
我想使用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()
returns 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 it_beg
的情况下用单行调用recursive_odd_stuff()
的基数?
似乎不可能编写 begin()
的 returns 左值的另一个版本,因为 "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)"。那么唯一的办法就是用两行来调用它?
所以,有一种方法可以使它成为 one-liner,但我不推荐它:
#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-th 递归调用改变了引用,然后第 n-1 个调用者使用它来做某事。在这种情况下,我建议将代码拆分为两个函数:
odd_recursive_stuff(IT begin, IT end){
odd_recursive_stuff_impl(begin, end);
}
odd_recursive_stuff_impl(IT& begin, IT& end){
....
}
这公开了一个只需要迭代器的 public 接口。稍后当您以不需要参考的方式更改算法时,或者它也需要 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()
return 是对局部变量的引用,但它是 lambda 对象的局部变量,而不是 operator()
本身。因为 lambda 对象一直存在到表达式 (;
) 结束,所以调用是安全的。请注意,我使用 std::ref
作为 return 引用的快速方法,而无需明确提及 return 类型。 std::reference_wrapper<T>
然后隐式转换为 T&
.
return it;
按值会 return 而 [it=std::begin(vec)]()mutable ->decltype(it)&{...};
也不可能。 ->decltype(std::begin(vec))&{
有效,但很冗长。另一种选择是显式编写迭代器的类型或使用 using
,但那更糟。
超载!
有一个只接受右值的版本:
void odd_recursive_stuff (std::vector<int>::const_iterator&& it_cur,
std::vector<int>::const_iterator it_end);
…和一个接受 lvalue-references 的版本(并为你做额外的行):
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);
}
这与移动语义所基于的原则相同,因为复制和 move-constructors 的选择方式相同。
但您可能会考虑放弃这一切,只返回 it
的新值:
std::vector<int>::const_iterator
odd_recursive_stuff(std::vector<int>::const_iterator it_cur,
std::vector<int>::const_iterator it_end);
然后您可以随意丢弃它。
没有人真的希望迭代器被引用。