In which cases does cpp create a default copy constructor [duplicate] - c++

This question already has answers here:
Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator?
(3 answers)
Closed 2 months ago.
I am confused why this doesn't show an error because a copy of b is created but there is no copy constructor - struct A (line B c{b}; )
Did C++ create the copy constructor or is it something else in question? Thanks
#include <iostream>
using namespace std;
struct B{
int a = 0;
B(){cout << "3" << endl;}
~B(){cout << "5" << endl;}
};
struct A{
B b;
B c{b};
A(int a){cout << "4" << endl;}
A(){cout << "1" << endl;}
~A(){cout << "2" << endl;}
};
void foo(A y){
cout << "6" << endl;
}
void foo2(A& a){
cout << "7" << endl;
}
int main()
{
B a{};
A c{};
foo(2);
foo2(c);
return 0;
}`

From the documentation on copy constructors
If no user-defined copy constructors are provided for a class type (struct, class, or union), the compiler will always declare a copy constructor as a non-explicit inline public member of its class. This implicitly-declared copy constructor has the form T::T(const T&) if all of the following are true:
each direct and virtual base B of T has a copy constructor whose parameters are const B& or const volatile B&;
each non-static data member M of T of class type or array of class type has a copy constructor whose parameters are const M& or const volatile M&.
That is, C++ will create a copy constructor for you if it can (if each field can be copied) and if you haven't explicitly opted out or written one yourself.
You can opt out of a copy constructor (since C++11) with the delete keyword.
B(const B&) = delete;
This will forbid C++ from creating a copy constructor, even though you didn't define one.
In your case, B has no base classes, so the first bullet point does not apply. The only non-static member is of type int, which is not of class or array-of-class type and thus is trivial to copy.
It's very important to keep in mind that C++ will generate a copy constructor, even if it's not semantically correct to do so. For instance, if your class contains raw pointers, C++ is very likely to generate a copy constructor that does not do the right thing. That's because, for raw pointers, C++ doesn't understand Rule of Three/Five.
If your class owns a pointer, you should use std::unique_ptr (which has ownership semantics by default and will forbid copying, by the rules above). If your class shares ownership of a pointer, you should use std::shared_ptr, which will work correctly in the presence of a defaulted copy constructor. If your class borrows a pointer, you should consider using a reference type, and if that's not an option, you should clearly document your non-owning pointer and you should consider writing an explicit copy constructor if the default one doesn't satisfy Rule of Three/Five.
In your case, all you have is a (non-pointer) int, so the more complicated nuances don't come up. But if you keep learning C++, they will, so I recommend giving that Stack Overflow question a thorough read when you have the time.

Related

Why is a class trivially copyable with all private special member functions? [duplicate]

This question already has answers here:
What is the difference between is_trivially_copyable and is_trivially_copy_constructible?
(1 answer)
Which rules determine whether an object is trivially copyable
(1 answer)
Can C++ classes with deleted methods be trivially copyable?
(2 answers)
std::is_trivially_xxx which one imply another
(1 answer)
Closed 6 days ago.
Code is like:
#include <iostream>
#include <type_traits>
class A
{
A() = default;
A(const A&) = default;
A(A&&) = default;
A& operator=(const A&) = default;
A& operator=(A&&) = default;
~A() = default;
int a;
};
int main()
{
std::cout << std::boolalpha <<
std::is_trivially_copy_assignable_v<A> << " " <<
std::is_trivially_copy_constructible_v<A> << " " <<
std::is_trivially_move_assignable_v<A> << " " <<
std::is_trivially_move_constructible_v<A> << " " <<
std::is_trivially_destructible_v<A> << " " <<
std::is_trivially_copyable_v<A> << "\n";
return 0;
}
And the output is false false false false false true. But according to cppreference:
A trivially copyable class is a class that
has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator,
each eligible copy constructor is trivial
each eligible move constructor is trivial
each eligible copy assignment operator is trivial
each eligible move assignment operator is trivial, and
has a non-deleted trivial destructor.
So from the first four boolean outputs, none of the copy constructor, move constructor, copy assignment operator and move assignment operator is trivial, so it's supposed to be non-trivially-copyable; However, the last output boolean is true.
Note: You can make ~A() = default and A() = default public so that you can create objects, but the core question is still the same.
The question is not can an instance of this class actually be copied?
std::is_trivially_copyable answers
if you copied this instance with a bytewise copy, would the result be the same as copying using a copy operator?
std::is_trivially_copy_constructible_v<T> doesn't check whether T has a trivial copy constructor, which is what trivially-copyable is based on.
Instead it checks whether a definition of a variable of the form
T t(u);
where u is a variable of type const T, would be well-formed in a context unrelated to T and that the construction of the variable would use only trivial functions. This includes a check for accessibility and a check for a non-deleted accesible destructor.
The same applies in similar form to the other _constructible_ and _assignable_ traits. These traits are not directly related to is_trivially_copyable. The is_trivially_copyable trait tells you whether you can use memcpy to copy objects of the type. It doesn't tell you whether copy/move-construction/assignment will be possible and trivial, which is what the other traits are for.
This is because the class A has an eligible move constructor that is trivial. Moreover, it also has a trivial non-deleted destructor and so class A satisfies all the requirement quoted below from trivially copyable class:
A trivially copyable class is a class:
that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator
where each eligible copy constructor, move constructor, copy assignment operator, and move assignment
operator is trivial, and
that has a trivial, non-deleted destructor (11.4.7).
(emphasis mine)
Note the emphasis on at least which means that class A satisfied all of the 3 above requirements.
The destructor is trivial as it is not user-provided and satisfied all 3 conditions given below:
A destructor is trivial if it is not user-provided and if:
the destructor is not virtual
all of the direct base classes of its class have trivial destructors, and
for all of the non-static data members of its class that are of class type (or array thereof), each such
class has a trivial destructor.
Similarly, the move constructor is also trivial which is one of the requirements(the second one to be specific) for a trivially copyable class.
A copy/move constructor for class X is trivial if it is not user-provided and if:
class X has no virtual functions (11.7.3) and no virtual base classes (11.7.2), and
the constructor selected to copy/move each direct base class subobject is trivial, and
for each non-static data member of X that is of class type (or array thereof), the constructor selected to
copy/move that member is trivial;

is_trivially_copyable behaves differently between the constructor I implemented and the default

There is a demonstrative code for std::is_trivially_copyable
https://en.cppreference.com/w/cpp/types/is_trivially_copyable
void test()
{
struct A {
int m;
A(const A& o):m(o.m){}
};
struct D {
int m;
D(D const&) = default; // -> trivially copyable
D(int x) : m(x + 1) {}
};
std::cout << std::is_trivially_copyable<A>::value << '\n';
std::cout << std::is_trivially_copyable<D>::value << '\n';
}
A is not trivially copyable, D does. I implement A's copy constructor with the default behavior. What does cause the difference?
This is how it is defined in c++:
https://en.cppreference.com/w/cpp/language/copy_constructor#Trivial_copy_constructor
Trivial copy constructor
The copy constructor for class T is trivial if all of the following are true:
it is not user-provided (that is, it is implicitly-defined or
defaulted) ;
T has no virtual member functions;
T has no virtual base classes;
the copy constructor selected for every direct base of T is trivial;
the copy constructor selected for every non-static class type (or array of class type) member of T is trivial;
A trivial copy constructor for a non-union class effectively copies every scalar subobject (including, recursively, subobject of subobjects and so forth) of the argument and performs no other action. However, padding bytes need not be copied, and even the object representations of the copied subobjects need not be the same as long as their values are identical.
TriviallyCopyable objects can be copied by copying their object representations manually, e.g. with std::memmove. All data types compatible with the C language (POD types) are trivially copyable.
It is not trivial, because it is user defined. That is the rule.
The compiler is not required to figure out if your code is the same as it would have generated. That's your job to figure out. :-)

C++ copy constructor double call on member initialization

Consider the below code, where a composing class with another class as its member is being instantiated:
class CopyAble {
private:
int mem1;
public:
CopyAble(int n1) : mem1(n1) {
cout << "Inside the CopyAble constructor" << endl;
}
CopyAble(const CopyAble& obj) {
cout << "Inside the CopyAble copy constructor" << endl;
this->mem1 = obj.mem1;
return *this;
}
CopyAble& operator=(const CopyAble& obj) {
cout << "Inside the CopyAble assignment constructor" << endl;
this->mem1 = obj.mem1;
}
~CopyAble() {};
};
class CopyAbleComposer {
private:
CopyAble memObj;
public:
CopyAbleComposer(CopyAble m1) : memObj(m1) {
cout << "Composing the composer" << endl;
}
~CopyAbleComposer() {}
};
int main()
{
CopyAble ca(10);
CopyAbleComposer cac(ca);
return 0;
}
When I run this, I get the output:
Inside the CopyAble constructor
Inside the CopyAble copy constructor
Inside the CopyAble copy constructor
Composing the composer
Which means that the CopyAble copy constructor is being run twice - once when the CopyAble object is passed into the CopyAbleComposer constructor, and again when the initializer memObj(m1) runs.
Is this an idiomatic use of the copy constructor? It seems very inefficient that the copy constructor runs twice when we try to initialize a member object with a passed-in object of the same type, and it seems like a trap a lot of C++ programmers can easily fall into without realizing it.
EDIT: I don't think this is a duplicate of the question regarding passing a reference into the copy constructor. Here, we are being forced to pass a reference into a regular constructor to avoid duplicate object creation, my question was that is this generally known that class constructors in C++ should have objects passed in by reference to avoid this kind of duplicate copy?
You should accept CopyAble by reference at CopyAbleComposer(CopyAble m1), otherwise a copy constructor will be called to construct an argument. You should also mark it as explicit to avoid accidental invocations:
explicit CopyAbleComposer(const CopyAble & m1)
Pass-by-value and the associated copying is a pretty widely known property of C++. Actually, in the past C++ was criticized for this gratuitious copying, which happened silently, was hard to avoid and could lead to decreased performance. This is humorously mentioned e.g. here:
You accidentally create a dozen instances of yourself and shoot them all in the foot. Providing emergency medical assistance is impossible since you can't tell which are bitwise copies and which are just pointing at others and saying, "That's me, over there."
C++98
When any function/method is declared to receive an argument by value, this sort of copying happens. It doesn't matter if it's a constructor, a "stand-alone" function or a method. To avoid this, use a const reference:
CopyAbleComposer(const CopyAble& m1) : memObj(m1)
{
...
}
Note: even if you rearrange your code as below, one copy always remains. This has been a major deficiency in C++ for a long time.
CopyAbleComposer cac(CopyAble(10)); // initializing mem1 by a temporary object
C++11
C++11 introduced move semantics, which replaces the additional copy by a "move" operation, which is supposed to be more efficient than copy: in the common case where an object allocates memory dynamically, "move" only reassigns some pointers, while "copy" allocates and deallocates memory.
To benefit from optimization offered by move semantics, you should undo the "optimization" you maybe did for C++98, and pass arguments by value. In addition, when initializing the mem1 member, you should invoke the move constructor:
CopyAbleComposer(CopyAble m1) : memObj(std::move(m1)) {
cout << "Composing the composer" << endl;
}
Finally, you should implement the move constructor:
CopyAble(CopyAble&& obj) {
cout << "Inside the CopyAble move constructor" << endl;
this->mem1 = obj.mem1;
}
Then you should see that the "copy" message doesn't appear, and is replaced by the "move" message.
See this question for more details.
Note: In all these examples, the CopyAble objects are assumed to be much more complex, with copy and move constructors doing non-trivial work (typically, resource management). In modern C++, resource management is considered a separate concern, in the context of separation of concerns. That is, any class that needs a non-default copy or move constructor, should be as small as possible. This is also called the Rule of Zero.

Copy constructor not calling

When I read about copy initializing vs direct initializing here. copy constructor should call in copy initializing. why here copy constructor not calling?
#include <iostream>
using namespace std;
class A{};
class B{
public:
B(const A &a){cout << "B construct from A" << endl;}
B(const B &b){cout << "B copy constructor" << endl;}
};
int main(){
A a;
B b = a;
return 0;
}
This is Copy ElisionRef 1:.
Copy constructor calls while generating temporaries might be optimized by the compiler by creating objects inline and it is explicitly allowed by the C++ Standard.
This is nicely demonstrated in the standard with an example as well:
C++03 Standard 12.2 Temporary objects [class.temporary]
Para 2:
[Example:
class X {
// ...
public:
// ...
X(int);
X(const X&);
˜X();
};
X f(X);
void g()
{
X a(1);
X b = f(X(2));
a = f(a);
}
Here, an implementation might use a temporary in which to construct X(2) before passing it to f() using X’s copy-constructor; alternatively, X(2) might be constructed in the space used to hold the argument. Also, a temporary might be used to hold the result of f(X(2)) before copying it to `b usingX’s copyconstructor; alternatively,f()’s result might be constructed in b. On the other hand, the expressiona=f(a)requires a temporary for either the argument a or the result off(a)to avoid undesired aliasing ofa`. ]
Ref 1:
C++03 12.8 Copying class objects [class.copy]
Para 12:
When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects.....
Copy initialization is still subject to copy elision, and I'm guessing that's what's happening. Theoretically, a temporary B is constructed from a and the the copy constructor is used to create b from the temporary. In practice, the copy can be optimized out.
To test this, you can make the copy constructor private:
class B{
public:
B(const A &a){cout << "B construct from A" << endl;}
private:
B(const B &b){cout << "B copy constructor" << endl;}
};
and get a compilation error. This means the compiler expects the copy constructor to be accessible, but is not required to call it.
Copy elision is the only case where observed behavior can be altered.

C++ constructor syntax [duplicate]

This question already has answers here:
Is there a difference between copy initialization and direct initialization?
(9 answers)
Closed 1 year ago.
Simple question: are the following statements equivalent? or is the second one doing more implicit things behind the scenes (if so, what?)
myClass x(3);
myClass x = myClass(3);
Thanks!
They are not completely identical. The first is called "direct initialization" while the second is called "copy initialization".
Now, the Standard makes up two rules. The first is for direct initialization and for copy initialization where the initializer is of the type of the initialized object. The second rule is for copy initialization in other cases.
So, from that point of view both are termed in one - the first - rule. In the case where you have copy initialization with the same type, the compiler is allowed to elide a copy, so it can construct the temporary you create directly into the initialized object. So you can end up very well with the same code generated. But the copy constructor, even if the copy is elided (optimized out), must still be available. I.e if you have a private copy constructor, that code is invalid if the code in which it appears has no access to it.
The second is called copy-initialization, because if the type of the initializer is of a different type, a temporary object is created in trying to implicitly convert the right side to the left side:
myclass c = 3;
The compiler creates a temporary object of the type of myclass then when there is a constructor that takes an int. Then it initializes the object with that temporary. Also in this case, the temporary created can be created directly in the initialized object. You can follow these steps by printing messages in constructors / destructors of your class and using the option -fno-elide-constructors for GCC. It does not try to elide copies then.
On a side-note, that code above has nothing to do with an assignment operator. In both cases, what happens is an initialization.
The second one may or may not call for an extra myclass object construction if copy elision is not implemented by your compiler. However, most constructors, have copy elision turned on by default even without any optimization switch.
Note initialization while construction never ever calls the assignment operator.
Always, keep in mind:
assignment: an already present object gets a new value
initialization: a new object gets a value at the moment it is born.
In the second one, a temporary object is created first and then is copied into the object x using myClass's copy constructor. Hence both are not the same.
I wrote the following to try and illustrate understand what's going on:
#include <iostream>
using namespace std;
class myClass
{
public:
myClass(int x)
{
this -> x = x;
cout << "int constructor called with value x = " << x << endl;
}
myClass(const myClass& mc)
{
cout << "copy constructor called with value = " << mc.x << endl;
x = mc.x;
}
myClass & operator = (const myClass & that)
{
cout << "assignment called" << endl;
if(this != &that)
{
x = that.x;
}
return *this;
}
private:
int x;
};
int main()
{
myClass x(3);
myClass y = myClass(3);
}
When I compile and run this code I get the following output:
$ ./a.out
int constructor called with value x = 3
int constructor called with value x = 3
This would seem to indicate that there is no difference between the two calls made in the main function, but that would be wrong. As litb pointed out, the copy constructor must be available for this code to work, even though it gets elided in this case. To prove that, just move the copy constructor in the code above to the private section of the class definition. You should see the following error:
$ g++ myClass.cpp
myClass.cpp: In function ‘int main()’:
myClass.cpp:27: error: ‘myClass::myClass(const myClass&)’ is private
myClass.cpp:37: error: within this context
Also note that the assignment operator is never called.