std::distance 相对于减法迭代器有什么好处?

发布于 2024-08-19 10:13:23 字数 147 浏览 5 评论 0原文

我正在迭代一个向量,并且需要迭代器当前指向的索引。以下方法有何优缺点?

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

I'm iterating over a vector and need the index the iterator is currently pointing at. What are the pros and cons of the following methods?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

与风相奔跑 2024-08-26 10:13:23

我更喜欢 it - vec.begin() ,正是出于 Naveen 给出的相反原因:所以如果将向量更改为列表,它不会编译。如果在每次迭代期间都执行此操作,则很容易将 O(n) 算法转变为 O(n^2) 算法。

如果您在迭代期间不在容器中跳转,另一种选择是将索引保留为第二个循环计数器。

注意:it 是容器迭代器的通用名称,std::container_type::iterator it;

I would prefer it - vec.begin() precisely for the opposite reason given by Naveen: so it wouldn't compile if you change the vector into a list. If you do this during every iteration, you could easily end up turning an O(n) algorithm into an O(n^2) algorithm.

Another option, if you don't jump around in the container during iteration, would be to keep the index as a second loop counter.

Note: it is a common name for a container iterator,std::container_type::iterator it;.

情感失落者 2024-08-26 10:13:23

我更喜欢 std::distance(vec.begin(), it) ,因为它允许我在不更改任何代码的情况下更改容器。例如,如果您决定使用 std::list 而不是 std::vector,它不提供随机访问迭代器,您的代码仍将编译。由于 std::distance 根据迭代器特征选择最佳方法,因此您也不会出现任何性能下降。

I would prefer std::distance(vec.begin(), it) as it will allow me to change the container without any code changes. For example, if you decide to use std::list instead of std::vector which doesn't provide a random access iterator your code will still compile. Since std::distance picks up the optimal method depending on iterator traits you'll not have any performance degradation either.

莫言歌 2024-08-26 10:13:23

正如 UncleBens 和 Naveen 所表明的那样,两者都有充分的理由。哪一个“更好”取决于您想要什么行为:您想要保证恒定时间行为,还是希望它在必要时回退到线性时间?

it - vec.begin() 需要恒定时间,但 operator - 仅在随机访问迭代器上定义,因此代码根本无法使用列表迭代器进行编译,例如。

std::distance(vec.begin(), it) 适用于所有迭代器类型,但如果用于随机访问迭代器,则只能是常量时间操作。

两者都不是“更好”。使用能满足您需要的产品。

As UncleBens and Naveen have shown, there are good reasons for both. Which one is "better" depends on what behavior you want: Do you want to guarantee constant-time behavior, or do you want it to fall back to linear time when necessary?

it - vec.begin() takes constant time, but the operator - is only defined on random access iterators, so the code won't compile at all with list iterators, for example.

std::distance(vec.begin(), it) works for all iterator types, but will only be a constant-time operation if used on random access iterators.

Neither one is "better". Use the one that does what you need.

南…巷孤猫 2024-08-26 10:13:23

我喜欢这个:it - vec.begin(),因为对我来说它清楚地表明了“距起点的距离”。对于迭代器,我们习惯于用算术来思考,因此 - 符号是这里最清晰的指示符。

I like this one: it - vec.begin(), because to me it clearly says "distance from beginning". With iterators we're used to thinking in terms of arithmetic, so the - sign is the clearest indicator here.

深巷少女 2024-08-26 10:13:23

如果您已经将算法限制/硬编码为仅使用 std::vector::iteratorstd::vector::iterator,那么使用哪个并不重要您最终将使用的方法。您的算法已经具体化,超出了选择其中之一可以产生任何影响的程度。他们都做完全相同的事情。这只是个人喜好问题。我个人会使用显式减法。

另一方面,如果您希望在算法中保留更高程度的通用性,即允许将来有一天它可能应用于其他迭代器类型,那么最好的方法取决于您的意图。这取决于您希望对此处可以使用的迭代器类型进行多大的限制。

  • 如果您使用显式减法,您的算法将仅限于相当狭窄的迭代器类别:随机访问迭代器。 (这就是您现在从 std::vector 获得的结果)

  • 如果您使用 distance,您的算法将支持更广泛的迭代器类别:输入迭代器。< /p>

当然,计算非随机访问迭代器的距离在一般情况下是低效的操作(而对于随机访问迭代器来说,它与减法一样有效)。从效率角度来看,您的算法对于非随机访问迭代器是否有意义取决于您。如果由此产生的效率损失是毁灭性的,以至于使您的算法完全无用,那么您应该更好地坚持使用减法,从而禁止低效的使用并迫使用户寻找其他迭代器类型的替代解决方案。如果非随机访问迭代器的效率仍在可用范围内,那么您应该使用距离并记录该算法在随机访问迭代器上效果更好的事实。

If you are already restricted/hardcoded your algorithm to using a std::vector::iterator and std::vector::iterator only, it doesn't really matter which method you will end up using. Your algorithm is already concretized beyond the point where choosing one of the other can make any difference. They both do exactly the same thing. It is just a matter of personal preference. I would personally use explicit subtraction.

If, on the other hand, you want to retain a higher degree of generality in your algorithm, namely, to allow the possibility that some day in the future it might be applied to some other iterator type, then the best method depends on your intent. It depends on how restrictive you want to be with regard to the iterator type that can be used here.

  • If you use the explicit subtraction, your algorithm will be restricted to a rather narrow class of iterators: random-access iterators. (This is what you get now from std::vector)

  • If you use distance, your algorithm will support a much wider class of iterators: input iterators.

Of course, calculating distance for non-random-access iterators is in general case an inefficient operation (while, again, for random-access ones it is as efficient as subtraction). It is up to you to decide whether your algorithm makes sense for non-random-access iterators, efficiency-wise. It the resultant loss in efficiency is devastating to the point of making your algorithm completely useless, then you should better stick to subtraction, thus prohibiting the inefficient uses and forcing the user to seek alternative solutions for other iterator types. If the efficiency with non-random-access iterators is still in usable range, then you should use distance and document the fact that the algorithm works better with random-access iterators.

倚栏听风 2024-08-26 10:13:23

根据 http://www.cplusplus.com/reference/std/iterator/distance/ ,由于vec.begin()是一个随机访问迭代器,距离方法使用-运算符。

所以答案是,从性能的角度来看,它是相同的,但如果有人必须阅读和理解您的代码,那么使用 distance() 可能更容易理解。

According to http://www.cplusplus.com/reference/std/iterator/distance/, since vec.begin() is a random access iterator, the distance method uses the - operator.

So the answer is, from a performance point of view, it is the same, but maybe using distance() is easier to understand if anybody would have to read and understand your code.

彩扇题诗 2024-08-26 10:13:23

我只会将 - 变体用于 std::vector - 它的含义非常清楚,并且操作简单(只不过是一个指针减法)由语法表达(距离,另一方面,乍一看听起来像毕达哥拉斯,不是吗?)。正如 UncleBen 指出的,- 还可以充当静态断言,以防 vector 意外更改为 list

而且我认为这种情况更为常见——不过没有数据可以证明这一点。主要论点:it - vec.begin() 源代码更短 - 更少的打字工作,更少的空间消耗。很明显,您的问题的正确答案归根结底是一个品味问题,这是一个有效的论点。

I'd use the - variant for std::vector only - it's pretty clear what is meant, and the simplicity of the operation (which isn't more than a pointer subtraction) is expressed by the syntax (distance, on the other side, sounds like pythagoras on the first reading, doesn't it?). As UncleBen points out, - also acts as a static assertion in case vector is accidentially changed to list.

Also I think it is much more common - have no numbers to prove it, though. Master argument: it - vec.begin() is shorter in source code - less typing work, less space consumed. As it's clear that the right answer to your question boils down to be a matter of taste, this can also be a valid argument.

坦然微笑 2024-08-26 10:13:23

我刚刚发现这个: 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;
    }

I just discovered this: 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;
    }

悲歌长辞 2024-08-26 10:13:23

除了 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;

结构数据类型时

Beside int float string etc., you can put extra data to .second when using diff. types like:

std::map<unsigned long long int, glm::ivec2> voxels_corners;
std::map<unsigned long long int, glm::ivec2>::iterator it_corners;

or

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;

when

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

or

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

with right type || structure you can put anything in the .second including a index number that is incremented when doing an insert.

instead of

it_corners - _corners.begin()

or

std::distance(it_corners.begin(), it_corners)

after

it_corners = voxels_corners.find(index_first+bdif_x+x_z);

the index is simply:

int vertice_index = it_corners->second.y;

when using the glm::ivec2 type

or

int vertice_index = it_corners->second.i;

in case of the structure data type

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文