Reputation: 24766
As I knew until today there are four default things create when creating a new class. "Default constructor", "Destructor", "Copy constructor" and "Assignment operator". But today when I was going trough a C++ article, it said that there can be situations where the copy constructor is not create by default.
Upvotes: 6
Views: 3046
Reputation: 227370
1) Yes, there can be situations where the copy constructor is not created by default.
2) The conditions where the implicitly declared default constructor is deleted are laid out in 12.8 Copying and moving class objects [class.copy]:
12.8.7 is about how the declaration of other special member functions affect the implicitly declared copy constructor. Whenever the class declares a copy constructor, a move constructor or a move assignment operator. If it declares any one of those, then you don't get an implicitly declared one.
...
7 If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.
12.8.11 is about how the data members and base classes affect the implicitly declared copy constructor. Essentially, if the class has any data members or base classes that are not copyable, the implicitly declared copy constructor is delete
d:
11 An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/ move constructor for a class X is defined as deleted (8.4.3) if X has:
— a variant member with a non-trivial corresponding constructor and X is a union-like class,
— a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
— for the copy constructor, a non-static data member of rvalue reference type, or
...
3) You can declare and define (either by providing an implementation or default
ing a copy constructor, or a move copy constructor, or both.
Upvotes: 8
Reputation: 95459
Yes, that is correct. For example, if a member of the class is not copyable/assignable (e.g. a member has a private assignment operator and private copy constructor), then you won't be able to rely on a default copy constructor for the containing class. If you want the containing class to be copyable/assignable under those circumstances, then you need to define those operations explicitly.
That being said, in most cases, you should avoid passing by value. In most circumstances, you should be passing an object by constant reference or passing a smart pointer (e.g. std::unique_ptr
) of your object. Passing by value (and any code that does an unnecessary amount of copying) will produce less efficient code than cases where you are able to reuse existing implementations. Additionally, for polymorphic objects, pass-by-value causes "slicing" (the functionality is truncated from the runtime type to the declared type to which the object is being copied), and so pass-by-value is particularly dangerous and error-prone when operating with any data types that might possibly be inherited.
Edit
To clarify the above a little bit... in terms of passing by const-reference vs passing by value, the decision should depend on the size of the object and how expensive it is to copy. Boost provides a handy mechanism in "call traits" (call_traits<T>::param_type) to automatically select between a value and a const reference based on the size of an object. When making this decision, it's also useful to distinguish between value types (objects that behave similarly to primitives -- for example, by overloading various operators and that are copyable, assignable, and cannot be inherited) and user-defined polymorphic types. Whenever you have a type that declares a virtual method, as a general rule of thumb, that object should be passed by reference or const reference to avoid the slicing which I mentioned above.
In terms of passing by smart pointer, generally this is done when transferring or sharing ownership (otherwise you should generally just pass around a reference to the object in question).
Upvotes: 0