Search code examples
c++copy-constructorimplicit-conversioncopy-elisioncopy-initialization

Do implicit class-type conversions use the copy-constructor?


The following quote from my C++ book:

When we use direct initialization, we are asking the compiler to use ordinary function matching to select the constructor that best matches the arguments we provide. When we use copy initialization, we are asking the compiler to copy the right-hand operand into the object being created, converting that operand if necessary.

For me this bolded bit produces a bit of ambiguity. It makes it sound like the right-hand operand is converted to the class-type and then the copy-constructor is used, for instance;

string s = "hello";

would become...

string s = string("hello"); 

which uses the copy constructor. If this is true then my test program;

#include <iostream>
using namespace std;

class A{
public:
    A(const A& b): data(b.data) { cout << "The first way" << endl;}
    A(const char* c): data(c) { cout << "The second way" << endl;}
    string data;

};
int main(){
    A first("hello");
    A second = "sup";
}

Should be producing "The second way, The second way, The first way". However it instead prints "The second way, The second way". From this I would've concluded it is using the const char* constructor and not the copy-constructor. I would be fine with this except later it says...

During copy initialization, the compiler is permitted (but not obligated) to skip the copy/move constructor and create the object directly. That is, the compiler is permitted to rewrite

string null_book = "9-999-99999-9"; 

into

string null_book("9-999-99999-9");

However, even if the compiler omits the call to the copy/move constructor, the copy/move constructor must exist and must be accessible (e.g., not private) at that point in the program.

I'm not sure why the copy constructor even needs a mention in these examples, doesn't

 string null_book = "9-999-99999-9"

always implicitly mean that the const char* constructor is being used anyway? Indeed it makes less sense to me that the copy-constructor needs to be defined in order for the above to work. But alas, if I put the "const A&" constructor as private (the rest public) then my program will not run. Why must the copy-constructor be defined for implicit conversions that don't even involve it? What constructor does "string null_book = "9-999-99999-9"" use?


Solution

  • string null_book = "9-999-99999-9"; means string null_book = string("9-999-99999-9");.

    This uses the const char * constructor to construct a temporary object, and then null_book is copy/move constructed from the temporary object, and then the temporary object is destroyed.

    (Copy/move construction means that a move constructor is used if available; otherwise a copy constructor).

    However this scenario is also eligible for copy elision. You actually quoted the copy elision specification in your question so I won't repeat it.

    The compiler may choose to use the same memory space for both null_book and the temporary object, and omit the calls to the temporary object destructor and the null_book copy/move constructor.

    In your case the compiler did indeed choose to do that, which is why you do not see any copy constructor output.

    Some compilers allow copy elision to be disabled by a switch, e.g. gcc/clang -fno-elide-constructors.

    More info about copy elision