#include <cstddef>
#include <cstdint>
#include <iostream>

template<size_t bytesLeft>
void doXor(void *p1, void *p2) {
	if constexpr(bytesLeft >= 8) {
		*reinterpret_cast<uint64_t*>(p1) ^= *reinterpret_cast<uint64_t*>(p2);
		doXor<bytesLeft - 8>(reinterpret_cast<uint8_t*>(p1) + 8, reinterpret_cast<uint8_t*>(p2) + 8);
	} else if constexpr(bytesLeft >= 4) {
		*reinterpret_cast<uint32_t*>(p1) ^= *reinterpret_cast<uint32_t*>(p2);
		doXor<bytesLeft - 4>(reinterpret_cast<uint8_t*>(p1) + 4, reinterpret_cast<uint8_t*>(p2) + 4);
	} else if constexpr(bytesLeft != 0) {
		*reinterpret_cast<uint8_t*>(p1) ^= *reinterpret_cast<uint8_t*>(p2);
		doXor<bytesLeft - 1>(reinterpret_cast<uint8_t*>(p1) + 1, reinterpret_cast<uint8_t*>(p2) + 1);
	}
}

template<typename T>
T& operator^=(T& a, T& b) {
	doXor<sizeof(T)>(&a, &b);
	return a;
}

struct A {
	uint64_t a;
	uint64_t b;
	uint64_t c;
	uint64_t d;
};

int main() {
	A a{1,2,3,4};
	A b{5,6,7,8};

	std::cout << a.a << " " << a.b << " " << a.c << " " << a.d << std::endl;
	std::cout << b.a << " " << b.b << " " << b.c << " " << b.d << std::endl;

	a ^= b;
	b ^= a;
	a ^= b;

	std::cout << a.a << " " << a.b << " " << a.c << " " << a.d << std::endl;
	std::cout << b.a << " " << b.b << " " << b.c << " " << b.d << std::endl;
}