我正在迭代一个向量,需要迭代器当前指向的索引。 AFAIK可以通过两种方式完成:
it - vec.begin()
std::distance(vec.begin(), it)
这些方法的优点和缺点是什么?
答案 0 :(得分:493)
我更倾向于it - vec.begin()
正是由于Naveen给出的相反原因:所以如果将向量更改为列表,将不会编译。如果你在每次迭代中都这样做,你很容易就会把O(n)算法变成O(n ^ 2)算法。
另一种选择,如果你在迭代期间没有在容器中跳转,那就是将索引保持为第二个循环计数器。
注意:it
是容器迭代器std::container_type::iterator it;
的通用名称。
答案 1 :(得分:125)
我更喜欢std::distance(vec.begin(), it)
因为它允许我在没有任何代码更改的情况下更改容器。例如,如果您决定使用std::list
而不是std::vector
而不提供随机访问迭代器,则代码仍将进行编译。由于std :: distance根据迭代器特性选择最佳方法,因此也不会有任何性能下降。
答案 2 :(得分:70)
正如UncleBens和Naveen所表明的那样,两者都有充分的理由。哪一个“更好”取决于你想要的行为:你想保证恒定时间行为,还是希望它在必要时回退到线性时间?
it - vec.begin()
需要一段时间,但operator -
仅在随机访问迭代器上定义,因此代码根本不会使用列表迭代器进行编译。
std::distance(vec.begin(), it)
适用于所有迭代器类型,但如果在随机访问迭代器上使用,则只能是一个常量操作。
两者都不“更好”。使用你所需要的那个。
答案 3 :(得分:11)
我喜欢这个:it - vec.begin()
,因为对我而言,它清楚地表示“距离开始的距离”。对于迭代器,我们习惯于在算术方面进行思考,因此-
符号是最清晰的指标。
答案 4 :(得分:7)
如果您已经将算法限制/硬编码为仅使用std::vector::iterator
和std::vector::iterator
,那么最终使用哪种方法并不重要。您的算法已经具体化,超出选择其中一个可以产生任何差异的点。他们都做了完全相同的事情。这只是个人喜好的问题。我个人会使用显式减法。
另一方面,如果你想在你的算法中保持更高的通用性,即允许将来的某一天可能应用于某些其他迭代器类型,那么最好的方法取决于在你的意图。这取决于你想在这里使用的迭代器类型的限制程度。
如果使用显式减法,则算法将限制为一个相当窄的迭代器类:随机访问迭代器。 (这是你现在从std::vector
)
如果使用distance
,您的算法将支持更广泛的迭代器类:输入迭代器。
当然,为非随机访问迭代器计算distance
通常情况下是低效操作(同样,对于随机访问的操作,它与减法一样有效)。由效率决定你的算法是否对非随机访问迭代器有意义取决于你。由此产生的效率损失对于使算法完全无用是毁灭性的,然后你应该更好地坚持减法,从而禁止低效使用并迫使用户为其他迭代器类型寻找替代解决方案。如果非随机访问迭代器的效率仍然在可用范围内,那么您应该使用distance
并记录该算法在随机访问迭代器中更好地工作的事实。
答案 5 :(得分:4)
根据http://www.cplusplus.com/reference/std/iterator/distance/,由于vec.begin()
是随机访问迭代器,因此距离方法使用-
运算符。
所以答案是,从性能的角度来看,它是相同的,但如果有人必须阅读和理解你的代码,那么使用distance()
更容易理解。
答案 6 :(得分:3)
我只将-
变体用于std::vector
- 它的含义非常清楚,操作的简单性(不超过指针减法)由语法(distance
,在另一方面,听起来像一读的毕达哥拉斯,不是吗?)。正如UncleBen所指出的,如果-
意外更改为vector
,list
也会充当静态断言。
此外,我认为它更常见 - 但没有数字证明它。主参数:it - vec.begin()
在源代码中更短 - 更少的打字工作,更少的空间消耗。很明显,对你的问题的正确答案归结为一个品味问题,这可以也是一个有效的论据。
答案 7 :(得分:1)
除了 int float string 等,你可以在使用 diff 时将额外的数据放入 .second。类型如:
std::map<unsigned long long int, glm::ivec2> voxels_corners;
std::map<unsigned long long int, glm::ivec2>::iterator it_corners;
或
struct voxel_map {
int x,i;
};
std::map<unsigned long long int, voxel_map> voxels_corners;
std::map<unsigned long long int, voxel_map>::iterator it_corners;
什么时候
long long unsigned int index_first=some_key; // llu in this case...
int i=0;
voxels_corners.insert(std::make_pair(index_first,glm::ivec2(1,i++)));
或
long long unsigned int index_first=some_key;
int index_counter=0;
voxel_map one;
one.x=1;
one.i=index_counter++;
voxels_corners.insert(std::make_pair(index_first,one));
正确的类型 ||结构,您可以在 .second 中放置任何内容,包括在执行插入时递增的索引号。
代替
it_corners - _corners.begin()
或
std::distance(it_corners.begin(), it_corners)
之后
it_corners = voxels_corners.find(index_first+bdif_x+x_z);
索引很简单:
int vertice_index = it_corners->second.y;
使用 glm::ivec2 类型时
或
int vertice_index = it_corners->second.i;
在结构数据类型的情况下
答案 8 :(得分:0)
这里是一个示例,用于查找10个“全部”事件以及索引。认为这会有所帮助。
return WebClient.create(this.dataServiceUrl)
.method(request.method ?: HttpMethod.GET)
.uri(builder -> builder.path(requestedPath).queryParam("q", "12").build())
.header("Accept", "application/json, text/plain, */*")
.body(BodyInserters.fromValue(request.body))
.retrieve()
.awaitBody()
答案 9 :(得分:0)
我刚刚发现了这个:https://greek0.net/boost-range/boost-adaptors-indexed.html
for (const auto & element : str | boost::adaptors::indexed(0)) {
std::cout << element.index()
<< " : "
<< element.value()
<< std::endl;
}