0

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;
}`
Xavi
  • 57
  • 8
  • 1
    https://en.cppreference.com/w/cpp/language/copy_constructor Has a list of requirements for implicitly declaring an copy constructor – UnholySheep Dec 17 '22 at 20:58

1 Answers1

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.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116