获取std :: vector的迭代器索引的最有效方法是什么?

时间:2010-01-28 07:40:41

标签: c++ iterator coding-style

我正在迭代一个向量,需要迭代器当前指向的索引。 AFAIK可以通过两种方式完成:

  • it - vec.begin()
  • std::distance(vec.begin(), it)

这些方法的优点和缺点是什么?

10 个答案:

答案 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::iteratorstd::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所指出的,如果-意外更改为vectorlist也会充当静态断言。

此外,我认为它更常见 - 但没有数字证明它。主参数: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;
    }