带有 Vector 迭代器的 For 循环是否复制值,使其效率低下?

问题描述

我正在使用 for 循环遍历向量中的所有元素,并且我已经看到了漂亮的代码

std::vector<int> vi;
// ... assume the vector gets populated
for(int i : vi) 
{
    // do stuff with i
}

但是,从快速测试来看,它似乎每次都将向量中的值复制到 i 中(我尝试在 for 循环中修改 i 并且向量保持不变)。

我问的原因是我实际上是用一个大结构的向量来做这个的。

std::vector<MyStruct> myStructList;
for(MyStruct onestruct : myStructList)
{
    cout << onestruct;
}

那么......考虑到内存复制量,这是一种糟糕的做事方式吗?使用传统索引是否更有效?

for(int i=0; i<myStructList.size(); i++)
{
    cout << myStructList[i];
}

谢谢,

解决方法

我在 Compiler Explorer 上对此进行了测试,发现即使使用 gcc 10.3 进行 -O3 优化,实际上也可以完成复制。

这是我的测试代码:

#include <iostream>
#include <vector>
using std::cout;

struct MyStruct {
    int a[32];
};

std::ostream& operator<<(std::ostream& s,const MyStruct& m) {
    for (int i = 0; i < 32; i++) s << m.a[i] << ' ';
    return s;
}

std::vector<MyStruct> myStructList;

void test(void) {
    for(MyStruct oneStruct : myStructList)
    {
        cout << oneStruct;
    }
}

这是the result的一部分:

test():
        pushq   %r13
        pushq   %r12
        pushq   %rbp
        pushq   %rbx
        subq    $152,%rsp
        movq    myStructList(%rip),%r12
        movq    myStructList+8(%rip),%r13
        cmpq    %r13,%r12
        je      .L8
        leaq    144(%rsp),%rbp
.L11:
        movdqu  (%r12),%xmm0
        movdqu  16(%r12),%xmm1
        leaq    16(%rsp),%rbx
        movdqu  32(%r12),%xmm2
        movdqu  48(%r12),%xmm3
        movdqu  64(%r12),%xmm4
        movdqu  80(%r12),%xmm5
        movups  %xmm0,16(%rsp)
        movdqu  96(%r12),%xmm6
        movdqu  112(%r12),%xmm7
        movups  %xmm1,32(%rsp)
        movups  %xmm2,48(%rsp)
        movups  %xmm3,64(%rsp)
        movups  %xmm4,80(%rsp)
        movups  %xmm5,96(%rsp)
        movups  %xmm6,112(%rsp)
        movups  %xmm7,128(%rsp)
.L10:
        movl    (%rbx),%esi
        movl    $_ZSt4cout,%edi
        addq    $4,%rbx
        call    std::basic_ostream<char,std::char_traits<char> >::operator<<(int)
        movl    $1,%edx
        leaq    15(%rsp),%rsi
        movb    $32,15(%rsp)
        movq    %rax,%rdi
        call    std::basic_ostream<char,std::char_traits<char> >& std::__ostream_insert<char,std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> >&,char const*,long)
        cmpq    %rbp,%rbx
        jne     .L10
        subq    $-128,%r12
        cmpq    %r12,%r13
        jne     .L11
.L8:
        addq    $152,%rsp
        popq    %rbx
        popq    %rbp
        popq    %r12
        popq    %r13
        ret

.L10:jne .L11 之间的线对应于 operator<< 函数,可以看到在此之前进行了大复制。

您应该在 & 循环中的 MyStructoneStruct 之间添加 for 以使其成为参考并避免不需要的数据副本。以下是添加了 &a result 的一部分:

test():
        pushq   %r12
        pushq   %rbp
        pushq   %rbx
        subq    $16,%rbp
        movq    myStructList+8(%rip),%rbp
        je      .L8
        subq    $-128,%rbp
.L11:
        leaq    -128(%rbp),%rbx
.L10:
        movl    (%rbx),%rbx
        jne     .L10
        leaq    128(%rbp),%rax
        cmpq    %rbp,%r12
        je      .L8
        movq    %rax,%rbp
        jmp     .L11
.L8:
        addq    $16,%rsp
        popq    %rbx
        popq    %rbp
        popq    %r12
        ret

现在你可以看到大复制被消除了,指向结构的指针直接用于operator<<的执行。