c constructor and copy constructor

0 votes
/* code block 1 */
#include <iostream>
class A
{
        private:
            int value;

        public:
            A(int n) { std::cout << "int n " << std::endl; value = n; }
            A(const A &other) { std::cout << " other " << std::endl; value = other.value; }
            // A (A &&other) { std::cout  << "other rvalue" << std::endl; value = other.value; }

            void print(){ std::cout << "print " << value << std::endl; }

};

int main(int argc, char **argv)
{
        A a = 10;
        A b = a;
        b.print();
        return 0;
}

When I compile the code above, it works as I expect

/* code block 2 */
g++ -std=c++11 t.cpp 
./a.out
int n 
 other 
print 10

When I remove the const from copy constructor

/* code block 3 */
class A
{
        ...
        A(int n) { std::cout << "int n " << std::endl; value = n; }
        A(A &other) { std::cout << " other " << std::endl; value = other.value; }
        // A (A &&other) { std::cout  << "other rvalue" << std::endl; value = other.value; }
}

The compiler won't compile

/* code block 4 */
t.cpp:19:5: error: no viable constructor copying variable of type 'A'
            A a = 10;
              ^   ~~
t.cpp:9:4: note: candidate constructor not viable: no known conversion from 'A' to 'int' for 1st argument
                    A(int n) { std::cout << "int n " << std::endl; value = n; }
                    ^
t.cpp:10:4: note: candidate constructor not viable: expects an l-value for 1st argument
                    A(A &other) { std::cout << " other " << std::endl; value = other.value; }

from the result t.cpp:9:4, it seems the compiler try to convert A to int, but the code is A a = 10;, If I am the compiler, I will either

  1. trying to initialized a temporary variable with type A from integer 10, and then use copy constructor A(A &other) to initialize a

  2. initialize a with constructor function A(int) directly

I am confusing about the compiler's output from t.cpp:9:4

from the output t.cpp:10:4, compiler says it expect an l-value copy constructor, so I change to code to

/* code block 5 */
class A
{
        ...
        A(int n) { std::cout << "int n " << std::endl; value = n; }
        A(A &other) { std::cout << " other " << std::endl; value = other.value; }
        A (A &&other) { std::cout  << "other rvalue" << std::endl; value = other.value; }
}

Questions:

  1. (in code block 3) why can't I remove the const from copy constructor?
  2. (in code block 4 -> t.cpp:9:4) why would the compiler try to convert from 'A' to 'int'?
  3. (in code block 5) the compiler says that it need a rvalue copy constructor(from code block 4 -> t.cpp:10:4), so I define one, but the running output show the rvalue copy constructor wasn't called, why?
Jun 22, 2022 in C++ by Nicholas
• 7,760 points
830 views

1 answer to this question.

0 votes

In a pre-C++17 compiler, this is known as copy elision (test it with C++17 on compiler explorer or wandbox with -std=c++17 vs. -std=c++14 flags). 

As of C++17, the compiler must delete numerous instances of copy and move constructors and create objects directly without the need of intermediary objects.

Unlike

A a { 10 };

the line

A a = 10;

means that a temporary object is constructed first, as if the code has:

A a = A(10);

Until C++17, the compiler could optimise this code and create a straight from 10 without using a temporary object. 

It should be noted that doing this copy elision optimization was permitted but not needed. 

You've seen that this allowed for optimization.

Regardless matter whether it chose to conduct copy elision or not, the compiler had to compile or fail the code. 

If the compiler was unable to invoke the copy function Object() { [native code] }, as in your example, it was forced to fail the compilation unconditionally, even if copy elision was chosen. 

With C++17, the compiler is now required to perform the copy elision optimization in this scenario. 

Because the copy function Object() { [native code] } is guaranteed to be elided, no copy function Object() { [native code] } is necessary, and the code can compile without problem.

Note the copy constructor without const:

A(A &other) { std::cout << " other " << std::endl; value = other.value; }

Without copy elision, this copy constructor can't be used for:

A a = A(10);

It can't be used because A(10) is a temporary object, and as such can be passed as an rvalue parameter to constructors and methods like

A(A && other);
foo(A && other);

or passed as a const lvalue reference parameter to constructors and methods like

A(const A& other);
bar(const A& other);

But it can't be passed as a regular mutable parameter (like in your code block 3).

With copy elision it does not even try to call the copy or the move constructor in these cases.

It still needs to call the copy constructor for

A b = a;

and it can do that with a mutable parameter, only because a is neither a temporary nor a const object. If you make a const then the code will fail to compile, when the copy constructor does not get a const (for C++17 and earlier):

const A a = 10;
A b = a;
//  ^^  this will fail

Fun note: The following line will is guaranteed not to call the copy constructor even once with C++17:

A a = A(A(A(A(1))));
answered Jun 27, 2022 by Damon
• 4,960 points

Related Questions In C++

0 votes
1 answer

The Definitive C++ Book Guide and List

For Beginner (includes those without coding experience) Programming: ...READ MORE

answered Jun 6, 2022 in C++ by pranav
• 2,590 points
484 views
0 votes
0 answers

Constructor Overloading in C++

My C++ overloading does not work as I expect it to: #include "Node.h" #include <iostream> Node::Node() { cout ...READ MORE

Jun 2, 2022 in C++ by Nicholas
• 7,760 points
219 views
0 votes
1 answer

functions in c++ call by value and call by reference

Calling a function by value copies the argument and stores it in a local variable for use by the function, so the argument is unaffected if the value of the local variable changes.  The argument is passed to the function as a reference rather than a copy, so if the function changes the value of the argument, the argument is also changed.   The void change(int); function prototype informs the compiler that there is a function named change that takes a single int argument and returns void (i.e. nothing).  Because there is no & with the argument, it is called by value.  You have the line change(orig); later in your code, which actually calls the function with. Take a look at the output of ...READ MORE

answered Jun 7, 2022 in C++ by Damon
• 4,960 points
417 views
0 votes
0 answers

Use of min and max functions in C++

Are std::min and std::max better than fmin ...READ MORE

Jun 2, 2022 in C++ by Nicholas
• 7,760 points
317 views
0 votes
0 answers

Is hiding implementation detail Encapsulation or Abstraction?

I have seen some people defining abstraction ...READ MORE

May 6, 2022 in Java by narikkadan
• 63,420 points
2,451 views
0 votes
1 answer

Why would anyone use set instead of unordered_set?

Unordered sets must compensate for their O(1) ...READ MORE

answered Jun 1, 2022 in C++ by Damon
• 4,960 points
2,221 views
0 votes
1 answer

What is a smart pointer and when should I use one?

A smart pointer is similar to a ...READ MORE

answered Jun 2, 2022 in C++ by Damon
• 4,960 points
260 views
0 votes
1 answer

The new syntax "= default" in C++11

A defaulted default function Object() { [native code] } is defined as a user-defined default function Object() { [native code] } with an empty compound statement and no initialization list. I'll give you an example to demonstrate the difference: #include <iostream> using namespace std; class A { public: ...READ MORE

answered Jun 7, 2022 in C++ by Damon
• 4,960 points
302 views
0 votes
1 answer

why are copy constructors needed and what are the cases where they are very helpful?

A copy constructor is a member function ...READ MORE

answered May 31, 2022 in C++ by Damon
• 4,960 points
450 views
0 votes
1 answer

Cases of static and dynamic binding in C++

When an object's static type is used to associate it with a member function, this is known as static binding (understand the type of its class). When a pointer or reference is associated with a member function based on the dynamic type of the object, this is known as dynamic binding (understand the instance of the variable at runtime). Before continuing, keep in mind that dynamic binding only works with pointers, references, and virtual functions for the base class. Because everything needed to call the function is known at compile time, the first call is a static binding (also known as early binding). Derived1 d1(1, 10); d1.display_data(); You already know that the d1 instance is a Derived1 automatic variable, and that it will call the Derived1::display data method (). The first condition is incorrect: d1 is neither a pointer nor a reference. The second condition isn't acceptable:  There is no virtual Derived1::display data. The second call is for ...READ MORE

answered Jun 7, 2022 in C++ by Damon
• 4,960 points
448 views
webinar REGISTER FOR FREE WEBINAR X
REGISTER NOW
webinar_success Thank you for registering Join Edureka Meetup community for 100+ Free Webinars each month JOIN MEETUP GROUP