How are copy constructors used and why are they important? [closed]

What is a copy constructor?

Quoting the C++11 standard, §12.8/2:

“A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&,
volatile X& or const volatile X&, and either there are no other parameters or else all other parameters
have default arguments”

So there are 4 different forms. Of these the X const& form is the one most often used. This is also the form of the implicit copy constructor that is generated (if possible) if no copy constructor is declared for the class.

A copy constructor is used for copying an object to a new instance of the class. A default copy constructor performs a member-wise copy, which is different from and more safe than just copying the bits. In particular a copy constructor or move constructor is logically used for a copy initialization, an initialization using the = sign like

Some_type x = expression;

In some circumstances the compiler is allowed to assume that copying is all that a copy constructor does, regardless of what it actually does. Logically needless copy constructions can then be removed, or as the standard calls it, elided. For example, if the expression above is the number 42, then instead of using that argument to construct a temporary and copying or moving that to x, the argument may be used to initialize x directly.

However, even when that is done, so that the copy constructor is not used, if the copy constructor would have been used except for the optimization then a copy constructor must exist and be accessible.

C++11 §12.2/1:

“Even when the creation of the temporary object is unevaluated (Clause 5)
or otherwise avoided (12.8), all the semantic restrictions shall be respected as if the temporary object had
been created and later destroyed.”


Is it necessary to call a destructor?

Re “Is it necessary to call a destructor if we [use a copy constructor]? ”.

Generally no.

Destructors in C++ are called automatically when an object goes out of scope or when it’s indirectly or directly destroyed via delete. The only exception is for placement new, which is a low level feature that has nothing in particular to do with copy constructors. Although placement new can involve copy construction.


A difference between C++03 and C++11 copy constructors.

To understand the difference between C++03 and C++11 regarding copy constructors you must be aware of three facts:

  • If a copy constructor isn’t declared, then it’s generated if possible. Hence by default every class has a copy constructor. It’s there.

  • Access is independent of overload resolution.
    Overload resolution chooses between all member functions, regardless of whether they’re accessible or not. Then access is checked. At that point you may get an error message about calling an inaccessible function, despite an expectation that overload resolution ideally should choose only from the accessible functions…

  • An ordinary function (or just function) is a better match than a an instantiation of a function template with the same signature.

Here is an example of overloading resolution choosing a private member function, in spite of a public function that could be called:

File [access_versus_overload_resolution.cpp]

 

class C
{
public:
    void foo( double );
    void foo( int );

    void bar( double );
private:
    void bar( int );
};

int main()
{
    C().foo( 42 );      // OK.
    C().bar( 42 );      //! Uh oh, inaccessible.
}

And here is an example of an ordinary function trumping a template instantiation:

File [function_versus_template.cpp]

 

using One = char[1];
using Two = char[2];

auto foo( char const* ) -> One&;
auto foo( char const (&)[10] ) -> Two&;

auto bar( char const* ) -> One&;

template< int n >
auto bar( char const (&)[n] ) -> Two&;

#include <iostream>
auto main() -> int
{
    using namespace std;
    //cout << sizeof( foo( "Blah blah" ) ) << endl;       //! Ambiguous
    cout << sizeof( bar( "Blah blah" ) ) << endl;       // "1"
}

With these facts at hand:

  • An inaccessible copy constructor will be chosen even if there is an accessible member function template that is just as good a match.
    (As a logical consequence of the general overload resolution rules.)

  • In C++11, a member function template instantiation will therefore be chosen only if it is a better match than the copy constructor, if any.

  • But in C++03 there was a special rule that ensured that with a conforming compiler a function member template would never be used to copy an object.

Here’s the C++03 wording, §12.8/3 in that standard, where I’ve added suitable emphasis:

“A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-
qualified) X and either there are no other parameters or else all other parameters have default arguments. A
member function template is never instantiated to perform the copy of a class object to an object of its class
type.

while in C++11 the wording is subtly changed, in that standard’s §12.8/6, where again I’ve added suitable emphasis:

“A declaration of a constructor for a class Xis ill-formed if its first parameter is of type (optionally cv-qualified)
X and either there are no other parameters or else all other parameters have default arguments. A member
function template is never instantiated to produce such a constructor signature.

I.e. no special injunction against function templates being used to copy objects in C++11.

Unfortunately for history buffs, the most readily available C++03 compiler for Windows, namely MinGW g++ of some old version such as 3.2, does not enforce the C++03 rule.

File [template_versus_cc.cpp]

 

class S
{
private:
    S( S const& );  // No such.

public:
    S() {}

    template< class T >
    S( T& ) {}
};

int main()
{
    S sm;
    S const sc;

    S s1( sm );     // OK in C++11, template is better match.
    S s2( sc );     //! Line 19. Copy constructor is selected, inaccessible.
}

Compiling with old g++:

[H:\dev\test\copy_construction]
> \bin\MinGW_3_1\bin\g++ --version | find "++"
g++ (GCC) 3.2.3 (mingw special 20030504-1)

[H:\dev\test\copy_construction]
> \bin\MinGW_3_1\bin\g++ -std=c++98 -pedantic template_versus_cc.cpp
template_versus_cc.cpp: In function `int main()':
template_versus_cc.cpp:4: `S::S(const S&)' is private
template_versus_cc.cpp:19: within this context

[H:\dev\test\copy_construction]
> _

By the C++03/C++98 rules the compiler should have flagged both copy initializations, at lines 18 and 19, but only flagged the latter, behaving as a C++11 compiler in this respect.

This is also what the more modern g++ 4.8.2 does, as well as Visual C++ 12.0. But a conforming C++03-compiler must diagnose both initializations.

Scott Meyers has written about this, citing two of the SO C++ Lounge dwellers as sources. Unfortunately the article yields the impressions that (1) it’s all about universal reference template arguments in C++11, and (2) there’s is no bug in g++. The former is wrong, as shown above, while the latter is literally correct. There was a bug, but as the standard has evolved, with C++11 the g++ behavior is now conforming…

Leave a Comment