I'm reviewing Stroustrup's code from C++ 4th Ed. Page 379 as updated in his errata sheet here. My questions are related to move constructor and assignment operations.
The move assignment invokes swap(*this, a)
and it's my understanding swap()
(per it's code below) invokes the move constructor and move assignment for the object. I tested the following code and believe swap()
ends up in never ending recursive calls to move constructor and assignment operator.
Is Stroustrup's example for the move assignment correct? Am I possibly invoking move assignment in an unexpected corner case? Basically, I'm confused on the never ending calls to move
through swap
.
UPDATE
Stroustrup uses a specialized version of swap()
. I found a similar version in his errata from 3rd edition, however it was not in the errata for my newer printing! I have provided the updated code which compiles and invokes the move assignment operator without infinite recursion.
Thank you
#include <iostream>
#include <memory>
using namespace std;
template<typename T, typename A = allocator<T>>
struct vector_base {
A alloc;
T* elem;
T* space;
T* last;
vector_base(const A& a, typename A::size_type n, typename A::size_type m =0)
: alloc{a}, elem{alloc.allocate(n+m)}, space{elem+n}, last{elem+n+m} { }
~vector_base() { alloc.deallocate(elem,last-elem); }
vector_base(const vector_base&) = delete;
vector_base& operator=(const vector_base&) = delete;
vector_base(vector_base&&);
vector_base& operator=(vector_base&&);
};
template<class T, class A> void swap(vector_base<T, A>& a, vector_base<T, A>& b)
{
swap(a.alloc,b.alloc);
swap(a.elem,b.elem);
swap(a.space,b.space);
swap(a.last,b.last);
}
template<typename T, typename A>
vector_base<T,A>::vector_base(vector_base&& a)
: alloc{a.alloc},
elem{a.elem},
space{a.space},
last{a.last}
{
cout << "move cons" << endl;
a.elem = a.space = a.last = nullptr;
}
template<typename T, typename A>
vector_base<T,A>& vector_base<T,A>::operator=(vector_base&& a)
{
cout << "move assig" << endl;
swap(*this,a);
return *this;
}
int main(int argc, char *argv[])
{
// expected to call move constructor with rvalue but elided
vector_base<int> vb1 = vector_base<int>(allocator<int>{}, 1);
// force move constructor, no elide
vector_base<int> vb2 = move(vector_base<int>(allocator<int>{}, 1));
vector_base<int> vb0(allocator<int>{}, 1);
// assign with rvalue, invoke move assignment
vb0 = vector_base<int>(allocator<int>{}, 1);
return 0;
}
Compilation:
g++ -std=c++11 -pedantic -Wall -g test143.cc && ./a.out
move cons
move assig
Defn for std::swap, do not use this version or it leads to infinite recursion!:
template <class T> void swap (T& a, T& b)
{
T c(std::move(a)); a=std::move(b); b=std::move(c);
}