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 letrhs
(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
andrhs
contents - Destroy
rhs
So passing by value is in this case more efficient than passing by reference.
Related Topics
Vc2013: Function from Bind Not Compiling
Error: Expression Must Have a Class Type
Getting a List of User Profiles on a Computer in C++ Win32
How Do C++ Progs Get Their Return Value, When a Return Is Not Specified in the Function
Check Xmm Register for All Zeroes
Right Justifying Output Stream in C++
Getting Input from User Using Cin
How to Tell If a Class/Struct Has No Data Members
What Is the Size of Sizeof(Vector)? C++
How to Convert a Time into Epoch Time
How Does Url Within Function Body Get Compiled
Undefined Reference to Template Members