C++11 - Using move semantics in a for loop?

Hi, there!

Yes, you read the title correctly. But why, you may ask now. Well, allow me to demonstrate:

for(unsigned int i = 0; i < vector.size(); i++)

As you can see, this is a basic for loop to iterate over elements of a vector.
But did you know there's also a bit faster way to iterate over the elements, like this:

for(unsigned int i = vector.size(); i--;)

Yep, it works, but I'd like to go to even more extremes. Check out this piece of code:

for(unsigned int&& i = vector.size(); i--;)

But now the ultimate question is, does it work properly?
Last edited on
do you not mean:
for(unsigned int i = vector.size();; i--)
and
for(unsigned int&& i = vector.size();; i--)
Script Coder wrote:

do you not mean:
for(unsigned int i = vector.size();; i--)
and
for(unsigned int&& i = vector.size();; i--)


How would those loops end?
They don't, or at least if they do its implementation (un?)defined, subtracting 1 from an unsigned int of value 0 (i.e making it negative) sets it to its highest value on my system.
Last edited on
did you know there's also a bit faster way to iterate over the elements

Taking a look at gcc and clang as the most readily-available compilers, yes, there is an improvement in case of Clang, not as good as switching to iterators though. Iterators are what most people expect to see, and that's what the compilers optimize for.

Check out this piece of code:

for(unsigned int&& i = vector.size(); i--;)

But now the ultimate question is, does it work properly?

It works properly, but it is exactly identical to your previous loop. Binding an rvalue reference to a size_t temporary doesn't involve anything that could be called "move semantics".


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <vector>
std::vector<int> v(100);
volatile int sink;
int main()
{
    for(unsigned int i = 0; i < v.size(); i++)
    {
            sink += v[i];
    }
}
// GCC loop:
.L6:
        movl    sink(%rip), %eax
        addl    $1, %edx
        addl    (%rdi,%rcx,4), %eax
        movl    %edx, %ecx
        cmpq    %rsi, %rcx
        movl    %eax, sink(%rip)
        jb      .L6
// CLang loop
.LBB1_2:
        movl    (%rax,%rsi,4), %esi
        addl    %esi, sink(%rip)
        movl    %edx, %esi
        incl    %edx
        cmpq    %rcx, %rsi
        jb      .LBB1_2
#include <vector>
std::vector<int> v(100);
volatile int sink;
int main()
{
    for(unsigned int i = v.size(); i--; )
    {
            sink += v[i];
    }
}
// GCC loop
.L8:
        movl    sink(%rip), %edx
        movl    %eax, %ecx
        subl    $1, %eax
        addl    (%rsi,%rcx,4), %edx
        cmpl    $-1, %eax
        movl    %edx, sink(%rip)
        jne     .L8

// Clang loop
.LBB1_2:
        movl    (%rcx), %edx
        addl    %edx, sink(%rip)
        addq    $-4, %rcx
        incl    %eax
        jne     .LBB1_2



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <vector>
std::vector<int> v(100);
volatile int sink;
int main()
{
    for(unsigned int&& i = v.size(); i--; )
    {
            sink += v[i];
    }
}
// GCC loop
.L8:
        movl    sink(%rip), %edx
        movl    %eax, %ecx
        subl    $1, %eax
        addl    (%rsi,%rcx,4), %edx
        cmpl    $-1, %eax
        movl    %edx, sink(%rip)
        jne     .L8
// Clang loop
.LBB1_2:
        movl    (%rcx), %edx
        addl    %edx, sink(%rip)
        addq    $-4, %rcx
        incl    %eax
        jne     .LBB1_2
#include <vector>
std::vector<int> v(100);
volatile int sink;
int main()
{
    for(auto i = v.begin(); i!=v.end(); ++i)
    {
            sink += *i;
    }
}
// GCC loop
.L8:
        movl    sink(%rip), %edx
        addl    (%rax), %edx
        addq    $4, %rax
        cmpq    %rax, %rcx
        movl    %edx, sink(%rip)
        jne     .L8
// Clang loop
.LBB1_1:
        movl    (%rcx), %edx
        addl    %edx, sink(%rip)
        addq    $4, %rcx
        cmpq    %rcx, %rax
        jne     .LBB1_1
Last edited on
Thank you for the replies! I didn't come to think of it that compilers would most likely optimize for loops this well. Assembly output convinced me.

I'm trying to get the gist of move semantics, so that's why I was wondering, if declaring variable "i" as an rvalue reference had some performance gain. From what I can understand, GCC compiler does optimization best in the last test. No rvalue references required.

Thanks a lot for the help!
Topic archived. No new replies allowed.