Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. Why isn't the copy constructor called

Why isn't the copy constructor called

Scheduled Pinned Locked Moved C / C++ / MFC
question
10 Posts 4 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Mircea NeacsuM Online
    Mircea NeacsuM Online
    Mircea Neacsu
    wrote on last edited by
    #1

    I feel I'm turning slightly mad. In my mind the two variants below should give the same output:

    class A {
    public:
    A () { cout << "A created" << endl; }
    };

    class B {
    public:
    B () { cout << "Default B constructor" << endl; }
    B (A oa) { cout << "B created from A" << endl; }
    B (const B& other) { cout << "B copy constructor" << endl; }
    };
    int main()
    {
    A aobj;
    B* bptr = new B (B (aobj));

    cout << "--------" << endl;

    B bobj (aobj);
    B* bbptr = new B (bobj);
    }

    After A is created, I create a B from A and then I copy the B using the copy constructor. At least that's my intention. However the output I get is:

    A created
    B created from A

    B created from A
    B copy constructor

    What am I missing here?

    Mircea

    _ L Mircea NeacsuM 3 Replies Last reply
    0
    • Mircea NeacsuM Mircea Neacsu

      I feel I'm turning slightly mad. In my mind the two variants below should give the same output:

      class A {
      public:
      A () { cout << "A created" << endl; }
      };

      class B {
      public:
      B () { cout << "Default B constructor" << endl; }
      B (A oa) { cout << "B created from A" << endl; }
      B (const B& other) { cout << "B copy constructor" << endl; }
      };
      int main()
      {
      A aobj;
      B* bptr = new B (B (aobj));

      cout << "--------" << endl;

      B bobj (aobj);
      B* bbptr = new B (bobj);
      }

      After A is created, I create a B from A and then I copy the B using the copy constructor. At least that's my intention. However the output I get is:

      A created
      B created from A

      B created from A
      B copy constructor

      What am I missing here?

      Mircea

      _ Offline
      _ Offline
      _Flaviu
      wrote on last edited by
      #2

      I might be wrong, but

      B (aobj)

      from

      new B (B (aobj))

      should call move constructor, not copy constructor ... P.S.

      B (B (aobj))

      would call a copy constructor only if what is inside of first brackets would be an already created B object, a l value, so, since

      B (aobj)

      is an anonymous object, move construct would be called. P.P.S. I added move constructor to B

      B(B&& rhs) { std::cout << "B move constructor" << std::endl; }

      and is never called either.

      Mircea NeacsuM 1 Reply Last reply
      0
      • Mircea NeacsuM Mircea Neacsu

        I feel I'm turning slightly mad. In my mind the two variants below should give the same output:

        class A {
        public:
        A () { cout << "A created" << endl; }
        };

        class B {
        public:
        B () { cout << "Default B constructor" << endl; }
        B (A oa) { cout << "B created from A" << endl; }
        B (const B& other) { cout << "B copy constructor" << endl; }
        };
        int main()
        {
        A aobj;
        B* bptr = new B (B (aobj));

        cout << "--------" << endl;

        B bobj (aobj);
        B* bbptr = new B (bobj);
        }

        After A is created, I create a B from A and then I copy the B using the copy constructor. At least that's my intention. However the output I get is:

        A created
        B created from A

        B created from A
        B copy constructor

        What am I missing here?

        Mircea

        L Offline
        L Offline
        Lost User
        wrote on last edited by
        #3

        I stepped through that code with the debugger, and it appears that it treats new B (B (aobj)); the same as new B (aobj);. That is to say, it optimises out the creation of the interim object, as it is never needed.

        Mircea NeacsuM 1 Reply Last reply
        0
        • _ _Flaviu

          I might be wrong, but

          B (aobj)

          from

          new B (B (aobj))

          should call move constructor, not copy constructor ... P.S.

          B (B (aobj))

          would call a copy constructor only if what is inside of first brackets would be an already created B object, a l value, so, since

          B (aobj)

          is an anonymous object, move construct would be called. P.P.S. I added move constructor to B

          B(B&& rhs) { std::cout << "B move constructor" << std::endl; }

          and is never called either.

          Mircea NeacsuM Online
          Mircea NeacsuM Online
          Mircea Neacsu
          wrote on last edited by
          #4

          The standard says that a move constructor is generated by default only for classes that don' t have a copy constructor. Otherwise the copy constructor is invoked. It has to be like that for compatibility reasons: move constructors were introduced only in C++11. As you did, I've also tried adding the move constructor with the same results :)

          Mircea

          1 Reply Last reply
          0
          • L Lost User

            I stepped through that code with the debugger, and it appears that it treats new B (B (aobj)); the same as new B (aobj);. That is to say, it optimises out the creation of the interim object, as it is never needed.

            Mircea NeacsuM Online
            Mircea NeacsuM Online
            Mircea Neacsu
            wrote on last edited by
            #5

            I was also ready to blame the compiler except that I was using the code in debug mode with all optimizations turned off. You can try it even with something like i=1;i=2; and you will see that it is generating not optimized code. Besides, my code is abstracted from a real world program where the copy constructor was making a deep copy of an object full of pointers and memory allocations. I cannot see the compiler simply throwing away all those side-effects. Also I tried with both VisualStudio and g++ and both compilers behave the same way. It must be something deeper in this than a compiler bug. As I use to say in these cases: the silly is on the other side of the screen :)

            Mircea

            1 Reply Last reply
            0
            • Mircea NeacsuM Mircea Neacsu

              I feel I'm turning slightly mad. In my mind the two variants below should give the same output:

              class A {
              public:
              A () { cout << "A created" << endl; }
              };

              class B {
              public:
              B () { cout << "Default B constructor" << endl; }
              B (A oa) { cout << "B created from A" << endl; }
              B (const B& other) { cout << "B copy constructor" << endl; }
              };
              int main()
              {
              A aobj;
              B* bptr = new B (B (aobj));

              cout << "--------" << endl;

              B bobj (aobj);
              B* bbptr = new B (bobj);
              }

              After A is created, I create a B from A and then I copy the B using the copy constructor. At least that's my intention. However the output I get is:

              A created
              B created from A

              B created from A
              B copy constructor

              What am I missing here?

              Mircea

              Mircea NeacsuM Online
              Mircea NeacsuM Online
              Mircea Neacsu
              wrote on last edited by
              #6

              A more thorough use of Google brought me the this:

              Quote:

              Whenever a temporary object is created for the sole purpose of being copied and subsequently destroyed, the compiler is allowed to remove the temporary object entirely and construct the result directly in the recipient (i.e. directly in the object that is supposed to receive the copy).

              This process is called elision of copy operation. It is described in [class.copy.elison] in the language standard. In this case B* bptr = new B(B(aobj)); can be transformed into B* bptr = new B(aboj); even if the copy constructor has side-effects.

              Mircea

              L Greg UtasG 2 Replies Last reply
              0
              • Mircea NeacsuM Mircea Neacsu

                A more thorough use of Google brought me the this:

                Quote:

                Whenever a temporary object is created for the sole purpose of being copied and subsequently destroyed, the compiler is allowed to remove the temporary object entirely and construct the result directly in the recipient (i.e. directly in the object that is supposed to receive the copy).

                This process is called elision of copy operation. It is described in [class.copy.elison] in the language standard. In this case B* bptr = new B(B(aobj)); can be transformed into B* bptr = new B(aboj); even if the copy constructor has side-effects.

                Mircea

                L Offline
                L Offline
                Lost User
                wrote on last edited by
                #7

                :thumbsup:

                1 Reply Last reply
                0
                • Mircea NeacsuM Mircea Neacsu

                  A more thorough use of Google brought me the this:

                  Quote:

                  Whenever a temporary object is created for the sole purpose of being copied and subsequently destroyed, the compiler is allowed to remove the temporary object entirely and construct the result directly in the recipient (i.e. directly in the object that is supposed to receive the copy).

                  This process is called elision of copy operation. It is described in [class.copy.elison] in the language standard. In this case B* bptr = new B(B(aobj)); can be transformed into B* bptr = new B(aboj); even if the copy constructor has side-effects.

                  Mircea

                  Greg UtasG Offline
                  Greg UtasG Offline
                  Greg Utas
                  wrote on last edited by
                  #8

                  I'm somewhat impressed by this compiler, but the compiler is allowed to means that it could behave differently under different compilers, especially if the construction of the temporary object has side effects.

                  Robust Services Core | Software Techniques for Lemmings | Articles
                  The fox knows many things, but the hedgehog knows one big thing.

                  <p><a href="https://github.com/GregUtas/robust-services-core/blob/master/README.md">Robust Services Core</a>
                  <em>The fox knows many things, but the hedgehog knows one big thing.</em></p>

                  Mircea NeacsuM 1 Reply Last reply
                  0
                  • Greg UtasG Greg Utas

                    I'm somewhat impressed by this compiler, but the compiler is allowed to means that it could behave differently under different compilers, especially if the construction of the temporary object has side effects.

                    Robust Services Core | Software Techniques for Lemmings | Articles
                    The fox knows many things, but the hedgehog knows one big thing.

                    Mircea NeacsuM Online
                    Mircea NeacsuM Online
                    Mircea Neacsu
                    wrote on last edited by
                    #9

                    On the contrary: I'm quite unimpressed ("we are not amused"). You know what they say that C makes it easy to shoot yourself in the foot and C++ gives you a bazooka. In this case I's say you walk with hand grenades in your pocket; safety pins are optional. Note that even in the variant that 'works':

                    B bobj (aobj);
                    B* bbptr = new B (bobj);

                    bobj is susceptible to being optimized out of existence and turned back into the previous case. To leave it to the compiler, or the compiler designer, if a function should be called or not, and to do it without even a warning, is unconscionable. Haven't researched the history of this section of the standard but I'd dare say that it dates back to the '03 time and the 'efficiency wars'. That was put to rest with the '11 move constructors and assignment operators but probably that section of the standard slipped through. In fairness, I should say that it's not something that happens too often. This is the first time I run into the issue but I'll keep it in mind for the future.

                    Mircea

                    Greg UtasG 1 Reply Last reply
                    0
                    • Mircea NeacsuM Mircea Neacsu

                      On the contrary: I'm quite unimpressed ("we are not amused"). You know what they say that C makes it easy to shoot yourself in the foot and C++ gives you a bazooka. In this case I's say you walk with hand grenades in your pocket; safety pins are optional. Note that even in the variant that 'works':

                      B bobj (aobj);
                      B* bbptr = new B (bobj);

                      bobj is susceptible to being optimized out of existence and turned back into the previous case. To leave it to the compiler, or the compiler designer, if a function should be called or not, and to do it without even a warning, is unconscionable. Haven't researched the history of this section of the standard but I'd dare say that it dates back to the '03 time and the 'efficiency wars'. That was put to rest with the '11 move constructors and assignment operators but probably that section of the standard slipped through. In fairness, I should say that it's not something that happens too often. This is the first time I run into the issue but I'll keep it in mind for the future.

                      Mircea

                      Greg UtasG Offline
                      Greg UtasG Offline
                      Greg Utas
                      wrote on last edited by
                      #10

                      Yes. I'm very unimpressed by the spec but still impressed that the compiler bothered to implement this "optimization".

                      Robust Services Core | Software Techniques for Lemmings | Articles
                      The fox knows many things, but the hedgehog knows one big thing.

                      <p><a href="https://github.com/GregUtas/robust-services-core/blob/master/README.md">Robust Services Core</a>
                      <em>The fox knows many things, but the hedgehog knows one big thing.</em></p>

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups