C++ Copy-Construct Construct-And-Assign Question

C++ copy-construct construct-and-assign question

The syntax

X a = b;

where a and b are of type X has always meant copy construction. Whatever variants, such as:

X a = X();

are used, there is no assignment going on, and never has been. Construct and assign would be something like:

X a;
a = X();

copy constructor and assignment operator in C++

Constructors, including copy constructors, are for initializing a class object. Assignment operators are for modifying an object which was already initialized.

Line (1) calls a copy constructor because it is initializing the object second. The assignment operator has nothing to do with the fact that the = symbol is also used in this syntax. Line (2) assigns to first which already exists, so uses the assignment operator.

In C++14 and earlier for line (3), the compiler is allowed to create a temporary object for the MyClass(2) expression, then use the copy constructor to initialize third, then destroy the temporary. But the compiler is also allowed to "elide" (remove) the temporary object, the copy constructor, and the temporary destructor, just initializing third directly in the same way as the original temporary. This copy elision is the one case where C++ allows an optimization which can cause a difference in observable behavior, since the copy constructor and/or destructor might have side effects (like printing traces) which the optimization skips.

In C++17 and later for line (3), we say that the expression MyClass(2) has result object third. So the expression specifies that third is direct-initialized with argument 2. No copy constructor or temporary object is involved. This feature of C++17 is often called "mandatory copy elision", since the behavior is the same as a C++14 program where the compiler was able to apply the copy elision optimization. But technically it's not a copy elision, since there is no copy involved to elide. There are still some other cases where copy elision is optional for the compiler.

Copy Constructors, Assignment Operator C++

If your instructor insists you must declare special members, but gives no guidance on how, then the best way is:

template <class T>
class BKS final
{
public:
~BKS() = default;
BKS(const BKS &) = default;
BKS& operator=(const BKS &) = default;
BKS(BKS &&) = default;
BKS& operator=(BKS &&) = default;

/* other members... */
};

If your instructor doesn't require you to declare them, but only requires they exist, the best way is

template <class T>
class BKS final
{
public:
/* other members... */
};

The copy constructor and assignment operator

No, they are different operators.

The copy constructor is for creating a new object. It copies an existing object to a newly constructed object.The copy constructor is used to initialize a new instance from an old
instance. It is not necessarily called when passing variables by value into functions
or as return values out of functions.

The assignment operator is to deal with an already existing object. The assignment operator is used to change an existing instance to have
the same values as the rvalue, which means that the instance has to be
destroyed and re-initialized if it has internal dynamic memory.

Useful link :

  • Copy Constructors, Assignment Operators, and More
  • Copy constructor and = operator overload in C++: is a common function possible?

C++ copy construct rvalue reference question

The reason why both fail to compile is that temporaries cannot be bound to non-const references.

In the first case, the expression abc() constructs a temporary abc object. Trying to pass it to fun() tries to bind the temporary to a non-const reference; this is disallowed.

The second case is effectively the same thing: the compiler tries to use 5 to construct an int temporary, but then fails to bind it to the non-const reference.

int const & a = 5; // allowed
int & b = 5; // disallowed

This was an intentional design decision, intended to prevent what would otherwise be very easy mistakes to make. Let's pretend this rule doesn't exist:

void add_one(double & i) {
i += 1;
}

void something() {
int x = 5;
add_one(x);
std::cout << x;
}

Wait... why is x 5 instead of 6!? Because x was used to construct a double temporary, which bound to the reference argument. add_one() added 1 to the temporary. Then the temporary is destroyed when control returns to something() and the value of x was never changed.

But, since temporaries cannot bind to non-const references, the add_one(x) line will instead fail to compile. This saves the programmer from having to figure out that an invisible temporary was implicitly created and destroyed on that one line of code.

Implementing the copy constructor in terms of operator=

If all members of MyClass have a default constructor, yes.

Note that usually it is the other way around:

class MyClass
{
public:
MyClass(MyClass const&); // Implemented
void swap(MyClass&) throw(); // Implemented
MyClass& operator=(MyClass rhs) { rhs.swap(*this); return *this; }
};

We pass by value in operator= so that the copy constructor gets called. Note that everything is exception safe, since swap is guaranteed not to throw (you have to ensure this in your implementation).

EDIT, as requested, about the call-by-value stuff: The operator= could be written as

MyClass& MyClass::operator=(MyClass const& rhs)
{
MyClass tmp(rhs);
tmp.swap(*this);
return *this;
}

C++ students are usually told to pass class instances by reference because the copy constructor gets called if they are passed by value. In our case, we have to copy rhs anyway, so passing by value is fine.

Thus, the operator= (first version, call by value) reads:

  • Make a copy of rhs (via the copy constructor, automatically called)
  • Swap its contents with *this
  • Return *this and let rhs (which contains the old value) be destroyed at method exit.

Now, we have an extra bonus with this call-by-value. If the object being passed to operator= (or any function which gets its arguments by value) is a temporary object, the compiler can (and usually does) make no copy at all. This is called copy elision.

Therefore, if rhs is temporary, no copy is made. We are left with:

  • Swap this and rhs contents
  • Destroy rhs

So passing by value is in this case more efficient than passing by reference.



Related Topics



Leave a reply



Submit