Swapping objects

The reference on std::swap disclaims that it uses a copy constructor and two assignment operators which may not be the most efficient way to swap two containers.

I was thinking about it and came up with this:
1
2
3
4
5
6
7
template <class T>
void swap(T& a, T& b)
{
  a ^= b;
  b ^= a;
  a ^= b;
}


It works for primitive types, but to work on classes the ^= operator needs to be overloaded. Is there a way to cast a complex object to raw binary data so that I can still perform bit-wise operations on it?

I know this sounds like a REALLY bad idea, but the more I think about it, the better it sounds to me. Example, a class containing an entire 15MB wave file would really just have the data in an array, aka a pointer. Using a bitwise XOR would swap the pointer, which swaps the data reference without the need to physically move that data. Even if that data is in a std::vector, at a very low level it's still all just pointers to data (sizeof doesn't change depending on the operations I perform).

The only part that may be funky would be the function/instruction pointers that are internal to the class. I'm not entirely sure how those would transfer over.
1. std::swap is defined in terms of move constructor and move assignment.

2. the xor swap hack is useless even for primitive types: plain std::swap is simpler and faster

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <utility>

template <class T>
void xor_swap(T& a, T& b)
{
  a ^= b;
  b ^= a;
  a ^= b;
}

int a = 1;
int b = 2;

int main()
{
//1.    xor_swap(a, b);
//2.    std::swap(a,b);
}


1
2
3
4
5
6
7
8
9
10
11
# xor_swap
main:
        movl    b(%rip), %edx
        movl    a(%rip), %eax
        xorl    %edx, %eax
        xorl    %eax, %edx
        xorl    %edx, %eax
        movl    %edx, b(%rip)
        movl    %eax, a(%rip)
        xorl    %eax, %eax
        ret
#std::swap
main:
        movl    a(%rip), %eax
        movl    b(%rip), %edx
        movl    %eax, b(%rip)
        movl    %edx, a(%rip)
        xorl    %eax, %eax
        ret


3. For classes, swap() is a fundamental member function, most well-designed C++ classes provide it along with copy/move constructors (see std::vector for example). Before moves became part of the language, they were implemented through swaps.

(edit, dropped the directives in the second paste)
Last edited on
Containers have an specialized version that swaps the pointers.

You can always obtain a pointer and read it as you please.
That's confusing, the only difference between those two codes are the three extra xorl instructions that I assume represent the three ^= that I put in there. I assume the rest is the function call stuff. So I'm confused on what the std::swap is even doing there.

Forgive my ignorance of ASM, but could it be hidden in that .cfg_startproc?

This is what it says according to my understanding:
1
2
3
4
5
6
7
8
9
10
11
# xor_swap
main:
        movl    b(%rip), %edx
        movl    a(%rip), %eax
        xorl    %edx, %eax
        xorl    %eax, %edx
        xorl    %edx, %eax
        movl    %edx, b(%rip)
        movl    %eax, a(%rip)
        xorl    %eax, %eax
        ret
function name
start here
check out b to edx
check out a to eax
a ^= b 
b ^= a
a ^= b
check in b to the original location
check in a to the original location
zero eax
end function


1
2
3
4
5
6
7
8
9
10
#std::swap
main:
.LFB18:
        .cfi_startproc
        movl    a(%rip), %eax
        movl    b(%rip), %edx
        movl    %eax, b(%rip)
        movl    %edx, a(%rip)
        xorl    %eax, %eax
        ret
function name
start here
??? -- Guessing this is linking to another .obj
??? --  that contains the std::swap instructions
check out a to eax
check out b to edx
check in b back to the origin
check in a back to the origin
zero eax
end function


But thanks for the replies. I know about the STL swap functions, but I'm still not quite sure why the xor hack won't work.

Anyways, it was just an academic question (I don't plan on implementing this in a project).
Last edited on
sorry, forgot to edit out the cfi_startproc in the second paste, it's not code
You should begin with some MemXor function and derive a MemSwap from it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
inline void MemXor(void * Dest, const void * Source, unsigned int Size)
{
    const unsigned char * Src = (const unsigned char *) Source;
    unsigned char * Dst =       (unsigned char *) Dest;
    for(unsigned int i = 0; i < Size; ++i)
    {
        Dst[i] ^= Src[i];
    }
}
// Then:
inline void MemSwap(void * First, void * Second, unsigned int Size)
{
    MemXor(First,Second,Size);
    MemXor(Second,First,Size);
    MemXor(First,Second,Size);
}
// And use it like:
MyType First;
MyType Second;
MemSwap(&First, &Second, sizeof(First));
Last edited on
> The only part that may be funky would be the function/instruction pointers that are internal to the class
┬┐what are you talking about?
> a class containing an entire 15MB wave file would really just have the data in an array, aka a pointer.
> Using a bitwise XOR would swap the pointer, which swaps the data reference
> without the need to physically move that data.

Define a noexcept destructor, noexcept move constructor and a noexcept move assignment operator and std::swap() would do the same thing.

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
29
struct A
{
    A( /* ... */ ) ;
    A( const A& ) ;
    ~A() noexcept ;
    A& operator= ( const A& ) ;

    A( A&& that ) noexcept : wav_data(nullptr), data_sz(0)
    {
        std::swap( wav_data, that.wav_data ) ;
        std::swap( data_sz, that.data_sz ) ;
    }

    A& operator= ( A&& that ) noexcept
    {
        std::swap( wav_data, that.wav_data ) ;
        std::swap( data_sz, that.data_sz ) ;
        return *this ;
    }

    private:
        unsigned char* wav_data ;
        std::size_t data_sz ;
};

void foo( A& one, A& two )
{
    std::swap( one, two ) ;
}



> I'm still not quite sure why the xor hack won't work.

Assume that it works flawlessly; you would still be just addressing one special case.

Supporting move semantics is far more general; with move, std::vector<A> etc. would also be able to eliminate gratuitous copying of objects of type A.
Topic archived. No new replies allowed.