0

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);
}
notaorb
  • 1,944
  • 1
  • 8
  • 18
  • _"it's my understanding swap invokes the copy constructor and assignment for the object being copied"_ ??? Why would it do that? – Asteroids With Wings Jun 14 '20 at 18:38
  • @AsteroidsWithWings see definition of swap just added at end of post. – notaorb Jun 14 '20 at 18:39
  • 1
    Don't know where you're pulling that "defn for swap" from, but that's not `std::swap` at all. I'll say what I've said to you before: **[read the documentation](https://en.cppreference.com/w/cpp/algorithm/swap)**. – Asteroids With Wings Jun 14 '20 at 18:39
  • 1
    What you are doing in `main` is initialization, not assignment. Assignment would be on a separate line from the object definition. – interjay Jun 14 '20 at 18:43
  • @AsteroidsWithWings I originally posted the C++98 swap. Updated with C++11 swap. – notaorb Jun 14 '20 at 18:47
  • So there you go, moves galore. – Asteroids With Wings Jun 14 '20 at 18:54
  • Somewhere in the book, look for a user-provided `swap` function that takes a pair of `vector_base`, and swaps their data members. The `swap` called in the move assignment operator can't be the stock `std::swap`, as that one itself uses move assignment and would have resulted in an infinite recursion. – Igor Tandetnik Jun 14 '20 at 19:03
  • @interjay thank you, I updated the code per your suggestion and move assignment is invoked, but it seems like in endless recursion! – notaorb Jun 14 '20 at 19:06
  • @IgorTandetnik thank you again! Thats what is so confusing. I'll have to search for the version of `swap`. Curious why Stroustrup didn't define it here. – notaorb Jun 14 '20 at 19:11
  • @interjay `vector_base vb1 = vector_base(allocator{}, 1);` this is initialization and results in the constructor being called in which `vb1` is initialized correct? I think I was confused because the right side looked like an rvalue, but it is not interpreted that way. Thanks! – notaorb Jun 14 '20 at 19:16
  • The right-hand side here is in fact an rvalue, and is interpreted this way. But the whole line is a declaration, and that right-hand side is an initializer, not an operand of an assignment. This line invokes a move constructor, not move assignment. If you want the latter, make it `vector_base vb1; vb1 = vector_base(allocator{}, 1);` – Igor Tandetnik Jun 14 '20 at 19:18
  • @the custom definition of `swap` may or may not be in the book, but it is almost certainly meant to be. The other chapter that uses essentially the same example was updated with a suitable definition of `swap`, see https://www.stroustrup.com/3rd_printing14.html – n. m. could be an AI Jun 14 '20 at 19:22
  • Actually, the line `vector_base vb1 = vector_base(allocator{}, 1);` doesn't invoke a move constructor either. The move constructor call is elided, and the line is equivalent to `vector_base vb1(allocator{}, 1);` – Igor Tandetnik Jun 14 '20 at 19:27
  • @IgorTandetnik `vector_base vb1 = vector_base(allocator{}, 1);` did not invoke the move constructor either. I tested with a debugger and inserting ouput code. Understood on move assignment, tested that code and it works. – notaorb Jun 14 '20 at 19:30
  • @IgorTandetnik regarding the move constructor elided, I agree and observe the same effect. Thanks again! – notaorb Jun 14 '20 at 19:31
  • @n.'pronouns'm. FYI - I updated this to use a working specialized version of swap, which properly invokes the move assignment operation. The other answer you posted lacks this information. Hopefully someone will find this question a useful answer with working code. – notaorb Jun 14 '20 at 19:48

0 Answers0