-1

I'm trying to make a program that implements the interaction between "copy and swap" idiom and and move control operations so I wrote this code:

class PInt
{
public:
    PInt(int = 0);
    PInt(const PInt&);
    PInt(PInt&&) noexcept;
    PInt& operator=(PInt);
    ~PInt();

    int* getPtr()const;

private:
    int* ptr;
    friend void swap(PInt&, PInt&);
};

PInt::PInt(int x) : 
    ptr(new int(x))
{
    std::cout << "ctor\n";
}

PInt::PInt(const PInt& rhs) :
    ptr(new int(rhs.ptr ? *rhs.ptr : 0))
{
    std::cout << "copy-ctor\n";
}

PInt::PInt(PInt&& rhs) noexcept :
    ptr(rhs.ptr)
{
    std::cout << "move-ctor\n";
    rhs.ptr = nullptr; // putting rhs in a valid state
}


PInt& PInt::operator=(PInt rhs)
{
    std::cout << "copy-assignment operator\n";
    swap(*this, rhs);
    return *this;
}

PInt::~PInt()
{
    std::cout << "dtor\n";
    delete ptr;
}

void swap(PInt& lhs, PInt& rhs)
{
    std::cout << "swap(PInt&, PInt&\n";
    using std::swap;
    swap(lhs.ptr, rhs.ptr);
}

PInt gen_PInt(int x)
{
    return {x};
}

int main()
{
    PInt pi1(1), pi2(2);
    //pi1 = pi2; // 1
    //pi1 = PInt{}; // 2
    //pi1 = std::move(pi2); // 3
    pi1 = std::move(PInt{}); // 4


}
  • Everything is OK for me so I think in 1 the copy-ctor is invoked by copy-asignment operator to initialize its parameter (it takes by value) then uses swap. in "2" I am assigning from an r-value thus I think the compiler applies some "Copy-elision" optimization; creating directly an object in the copy-assignment operator.

  • What I am not sure of is from 3 and 4. so here is the result of 3 and 4:

    un-commenting line 3:

    ctor
    ctor
    move - ctor
    copy - assignment operator
    swap(PInt&, PInt &
    dtor
    dtor
    dtor
    

Un-commenting line 4:

    ctor
    ctor
    ctor
    move - ctor
    copy - assignment operator
    swap(PInt&, PInt &
    dtor
    dtor
    dtor
    dtor
  • Why 3 and 4 use std::move but for gets n extra constructor called?

** Which is efficient: defining a copy/move-assignment operator taking by value or two separate versions : copy assignment and move assignment? Because the one version each time called it either calls (extra call) copy-ctor or move-ctor to initialize its parameter?

Maestro
  • 2,512
  • 9
  • 24
  • 3
    there is no `"Done!"` in the code. Please provide the code that matches the output – 463035818_is_not_an_ai Feb 11 '20 at 20:08
  • 2
    Note that your `operator=` outputs `copy-assignment operator`, but it actually supports both copy assignment and move assignment, since `PInt` has both a copy constructor and a move constructor. The choice to use copy vs move is handled at the call site depending on whether an lvalue or rvalue is being assigned. If you want to differentiate, define separate operators: `PInt& operator=(const PInt&)` for copy and `PInt& operator=(PInt&&)` for move – Remy Lebeau Feb 11 '20 at 20:12
  • @RemyLebeau: Is my version efficient as defining separate copy assignment and move-assignment? – Maestro Feb 11 '20 at 22:01
  • 1
    @Maestro they are about the same. I only mention this if you really needed to differentiate, such as for logging purposes, so that you can log copy vs move operations – Remy Lebeau Feb 12 '20 at 00:05

1 Answers1

4

Why 3 and 4 use std::move but for gets n extra constructor called?

The "extra" (move) constructor for 3 and 4 is to create the object that is the argument here:

PInt& PInt::operator=(PInt rhs)
                      ^^^^^^^^

The "extra" constructor for 4 is to create this temporary:

pi1 = std::move(PInt{}); // 4
                ^^^^^^
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • What I can see is if I define them separately then the move assignment doesn't call move constructor because it takes an rvalue reference while the one for two passes by value. The question: Which is more efficient: The copy/move assignment operator or defining separate ones? – Maestro Feb 11 '20 at 21:52
  • 1
    @Maestro move semantics are meant to be efficient. Your `PInt` class is very light-weight, so it doesn't really matter whether you implement a single copy+move assignment operator, or two separate copy/move operators. Obviously, in sheer number of instructions, implementing separate operators *may* avoid a set of constructor/destructor calls for a temp object, but the overhead is so minimal as to not really be an issue. What is more important is being able to write code you can maintain over time. – Remy Lebeau Feb 12 '20 at 00:08