Another base/derived problem with gcc, but not with MSVC

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Christian Engström

    Another base/derived problem with gcc, but not with MSVC

    When i compile the program listed below with gcc version 3.3.1 (MinGW on
    Windows XP) I get the following result:

    Calling 'func(d)':
    'base' copy constructor

    Calling 'func(*d_handle )':
    'base' copy constructor

    Calling 'func(d_handle) ':
    'derived' copy constructor <-- Why?
    'base' copy constructor

    The problem is the call to the 'derived' copy constructor, which I don't
    want and hadn't expected, and which Microsoft Visual C++ 6.0 doesn't
    generate.

    Is it the gcc or the MSVC compiler that is broken? I would naively have
    assumed that MSVC is right and gcc is wrong in this case, but perhaps
    there is something in the standard that prescribes the gcc behavior for
    some reason?

    My intention was that there shouldn't be any temporary 'derived' objects
    created at all, since both the implicit conversion operator from
    'derived_handle ' to 'derived' and 'derived's operator* return references
    to an existing 'derived' object.

    After having recived some expert answers on this list on a somewhat
    related question, I am nowadays aware of the rule that says that a
    temporary object can't be tied to a non-const reference function
    parameter, but that shouldn't have anything to do with this problem,
    should it?

    =============== =============== =============== =============== ===========
    #include <iostream>

    //----------------------------------------------------------------------------
    class base
    {
    public:
    base() {}
    base(const base&) {std::cout << " 'base' copy constructor" <<
    std::endl;}
    };


    //----------------------------------------------------------------------------
    class derived : public base
    {
    public:
    derived() {}
    derived(const derived&) {std::cout << " 'derived' copy constructor"
    << std::endl;}
    };


    //----------------------------------------------------------------------------
    class derived_handle
    {
    derived* tptr;

    public:
    derived_handle( ) {};
    derived_handle( derived* inptr) : tptr(inptr) {}

    derived& operator*() const {return *tptr;}
    derived* operator->() const {return tptr;}

    operator derived&() const {return **this;} // Implicit conversion
    to 'derived'
    };


    //----------------------------------------------------------------------------
    void func(base b) {} // Takes the parameter by value


    //=============== =============== =============== =============== =============== =
    int main (int argc, char* argv[])
    {
    derived d;
    derived_handle d_handle(new derived());

    std::cout << "\nCalling 'func(d)':" << std::endl;
    func(d);

    std::cout << "\nCalling 'func(*d_handle )':" << std::endl;
    func(*d_handle) ;

    std::cout << "\nCalling 'func(d_handle) ':" << std::endl;
    func(d_handle); // Calls 'derived' copy constructor with gcc (only)

    return 0;
    }

  • John Harrison

    #2
    Re: Another base/derived problem with gcc, but not with MSVC


    "Christian Engström" <christian.engs trom@glindra.or g> wrote in message
    news:yzPWb.8$dV 6.16427@news.ec rc.de...[color=blue]
    > When i compile the program listed below with gcc version 3.3.1 (MinGW on
    > Windows XP) I get the following result:
    >
    > Calling 'func(d)':
    > 'base' copy constructor
    >
    > Calling 'func(*d_handle )':
    > 'base' copy constructor
    >
    > Calling 'func(d_handle) ':
    > 'derived' copy constructor <-- Why?
    > 'base' copy constructor
    >
    > The problem is the call to the 'derived' copy constructor, which I don't
    > want and hadn't expected, and which Microsoft Visual C++ 6.0 doesn't
    > generate.
    >
    > Is it the gcc or the MSVC compiler that is broken? I would naively have
    > assumed that MSVC is right and gcc is wrong in this case, but perhaps
    > there is something in the standard that prescribes the gcc behavior for
    > some reason?
    >
    > My intention was that there shouldn't be any temporary 'derived' objects
    > created at all, since both the implicit conversion operator from
    > 'derived_handle ' to 'derived' and 'derived's operator* return references
    > to an existing 'derived' object.
    >
    > After having recived some expert answers on this list on a somewhat
    > related question, I am nowadays aware of the rule that says that a
    > temporary object can't be tied to a non-const reference function
    > parameter, but that shouldn't have anything to do with this problem,
    > should it?
    >
    > =============== =============== =============== =============== ===========
    > #include <iostream>
    >
    >[/color]
    //--------------------------------------------------------------------------
    --[color=blue]
    > class base
    > {
    > public:
    > base() {}
    > base(const base&) {std::cout << " 'base' copy constructor" <<
    > std::endl;}
    > };
    >
    >
    >[/color]
    //--------------------------------------------------------------------------
    --[color=blue]
    > class derived : public base
    > {
    > public:
    > derived() {}
    > derived(const derived&) {std::cout << " 'derived' copy constructor"
    > << std::endl;}
    > };
    >
    >
    >[/color]
    //--------------------------------------------------------------------------
    --[color=blue]
    > class derived_handle
    > {
    > derived* tptr;
    >
    > public:
    > derived_handle( ) {};
    > derived_handle( derived* inptr) : tptr(inptr) {}
    >
    > derived& operator*() const {return *tptr;}
    > derived* operator->() const {return tptr;}
    >
    > operator derived&() const {return **this;} // Implicit conversion
    > to 'derived'
    > };
    >
    >
    >[/color]
    //--------------------------------------------------------------------------
    --[color=blue]
    > void func(base b) {} // Takes the parameter by value
    >
    >
    >[/color]
    //=============== =============== =============== =============== ==============
    ==[color=blue]
    > int main (int argc, char* argv[])
    > {
    > derived d;
    > derived_handle d_handle(new derived());
    >
    > std::cout << "\nCalling 'func(d)':" << std::endl;
    > func(d);
    >
    > std::cout << "\nCalling 'func(*d_handle )':" << std::endl;
    > func(*d_handle) ;
    >
    > std::cout << "\nCalling 'func(d_handle) ':" << std::endl;
    > func(d_handle); // Calls 'derived' copy constructor with gcc (only)
    >
    > return 0;
    > }
    >[/color]

    I can't claim any expertise, I'm only answering because no-one else has.

    I think you are getting into obscure areas of C++ which you would be well
    advised to stay away from. Even if you could find out what the official
    position was there is no guarantee that compilers would implement it
    correctly.

    FWIW I believe that the position is this, compilers are allow to silently
    add or remove calls to copy constructors in certain circumstances (e.g.
    creation of temporaries, return value optimisation) EVEN IF those copy
    constructors have side effects. Essentially you must write your copy
    constructors in such a way that you don't mind if they are called on the odd
    occasion when you weren't expecting it (or not called when you were
    expecting it).

    For a definitive answer to your particular case I'd buy a copy of the C++
    standard or perhaps you could try your question again in
    comp.lang.c++.m oderated where the high powered folks hang out.

    john


    Comment

    • Christian Engström

      #3
      Re: Another base/derived problem with gcc, but not with MSVC

      John Harrison wrote:[color=blue]
      >
      > I can't claim any expertise, I'm only answering because no-one else has.
      >
      > I think you are getting into obscure areas of C++ which you would be well
      > advised to stay away from. Even if you could find out what the official
      > position was there is no guarantee that compilers would implement it
      > correctly.[/color]

      Yes, I seem to have run into a quite unlikely series of obscure compiler
      problems with this one.

      I *was* intending to design something that was slightly outside the
      well-trodden main road, but it appears that I have walked right up to
      the end of the world and that I'm beginning to fall over the edge. :-)

      [color=blue]
      > FWIW I believe that the position is this, compilers are allow to silently
      > add or remove calls to copy constructors in certain circumstances (e.g.
      > creation of temporaries, return value optimisation) EVEN IF those copy
      > constructors have side effects. Essentially you must write your copy
      > constructors in such a way that you don't mind if they are called on the odd
      > occasion when you weren't expecting it (or not called when you were
      > expecting it).[/color]

      Hm. I'm not arguing with what you say, but that would essentially mean
      that the behavior of any C++ program that contains a non-trivial copy
      constructor is undefined. And since most real world C++ programs do,...
      [color=blue]
      >
      > For a definitive answer to your particular case I'd buy a copy of the C++
      > standard or perhaps you could try your question again in
      > comp.lang.c++.m oderated where the high powered folks hang out.
      >
      > john[/color]

      Right, I'll try c.l.c++.moderat ed.

      Thanks a lot for the answer, anyway!

      /Christian

      Comment

      • John Harrison

        #4
        Re: Another base/derived problem with gcc, but not with MSVC

        >[color=blue][color=green]
        > > FWIW I believe that the position is this, compilers are allow to[/color][/color]
        silently[color=blue][color=green]
        > > add or remove calls to copy constructors in certain circumstances (e.g.
        > > creation of temporaries, return value optimisation) EVEN IF those copy
        > > constructors have side effects. Essentially you must write your copy
        > > constructors in such a way that you don't mind if they are called on the[/color][/color]
        odd[color=blue][color=green]
        > > occasion when you weren't expecting it (or not called when you were
        > > expecting it).[/color]
        >
        > Hm. I'm not arguing with what you say, but that would essentially mean
        > that the behavior of any C++ program that contains a non-trivial copy
        > constructor is undefined. And since most real world C++ programs do,...
        >[/color]

        If the non-trivial copy constructor has a side effect then yes. But most
        copy constructors (trivial or not) do nothing other than copy objects.

        john


        Comment

        • lilburne

          #5
          Re: Another base/derived problem with gcc, but not with MSVC



          Christian Engström wrote:
          [color=blue]
          > John Harrison wrote:
          >[color=green]
          >>
          >> I can't claim any expertise, I'm only answering because no-one else has.
          >>
          >> I think you are getting into obscure areas of C++ which you would be well
          >> advised to stay away from. Even if you could find out what the official
          >> position was there is no guarantee that compilers would implement it
          >> correctly.[/color]
          >
          >
          > Yes, I seem to have run into a quite unlikely series of obscure compiler
          > problems with this one.[/color]

          It looks like a gcc bug. The compiler is treating the returned derived&
          differently when it is returned from the conversion function

          operator derived&()

          to what it does when it calls dereference function:

          derived& operator*()

          even if operator derived&() is rewritten in the clearer form

          operator derived&() const {return operator*();}

          the extra constructor is called.


          Comment

          • Christian Engström

            #6
            Re: Another base/derived problem with gcc, but not with MSVC

            lilburne wrote:[color=blue]
            >
            > It looks like a gcc bug. The compiler is treating the returned derived&
            > differently when it is returned from the conversion function
            >
            > operator derived&()
            >
            > to what it does when it calls dereference function:
            >
            > derived& operator*()
            >
            > even if operator derived&() is rewritten in the clearer form
            >
            > operator derived&() const {return operator*();}
            >
            > the extra constructor is called.
            >[/color]

            Right, I have just reported to gcc it as a possible bug, and it recieved
            Bugzilla number 14140.

            I took the liberty of quoting your post in the bug report, I hope that
            was okay.

            /Christian

            Comment

            • Gavin Deane

              #7
              Re: Another base/derived problem with gcc, but not with MSVC

              "John Harrison" <john_andronicu s@hotmail.com> wrote in message news:<c0i974$17 u0tu$1@ID-196037.news.uni-berlin.de>...[color=blue][color=green]
              > >[color=darkred]
              > > > FWIW I believe that the position is this, compilers are allow to[/color][/color]
              > silently[color=green][color=darkred]
              > > > add or remove calls to copy constructors in certain circumstances (e.g.
              > > > creation of temporaries, return value optimisation) EVEN IF those copy
              > > > constructors have side effects. Essentially you must write your copy
              > > > constructors in such a way that you don't mind if they are called on the[/color][/color]
              > odd[color=green][color=darkred]
              > > > occasion when you weren't expecting it (or not called when you were
              > > > expecting it).[/color]
              > >
              > > Hm. I'm not arguing with what you say, but that would essentially mean
              > > that the behavior of any C++ program that contains a non-trivial copy
              > > constructor is undefined. And since most real world C++ programs do,...
              > >[/color]
              >
              > If the non-trivial copy constructor has a side effect then yes. But most
              > copy constructors (trivial or not) do nothing other than copy objects.[/color]

              Are you using "undefined" in the strict, nasal-demon sense of the
              word? Given this class and global variable:

              int global = 0;

              class foo
              {
              public:
              foo(double d) : d_(d) {}
              foo(const foo& other) : d_(other.d_) { ++global; }

              private:
              double d_;
              };

              there's no UB in copying foo objects is there? It's just that you've
              no way of knowing what the value of 'global' will be after an
              expression in which the copy constructor could be elided.

              Since I don't write copy constructors with side effects, I am asking
              purely out of academic interest.

              --
              GJD

              Comment

              • John Harrison

                #8
                Re: Another base/derived problem with gcc, but not with MSVC


                "Gavin Deane" <deane_gavin@ho tmail.com> wrote in message
                news:6d8002d0.0 402130653.48bc3 89d@posting.goo gle.com...[color=blue]
                > "John Harrison" <john_andronicu s@hotmail.com> wrote in message[/color]
                news:<c0i974$17 u0tu$1@ID-196037.news.uni-berlin.de>...[color=blue][color=green][color=darkred]
                > > >
                > > > > FWIW I believe that the position is this, compilers are allow to[/color]
                > > silently[color=darkred]
                > > > > add or remove calls to copy constructors in certain circumstances[/color][/color][/color]
                (e.g.[color=blue][color=green][color=darkred]
                > > > > creation of temporaries, return value optimisation) EVEN IF those[/color][/color][/color]
                copy[color=blue][color=green][color=darkred]
                > > > > constructors have side effects. Essentially you must write your copy
                > > > > constructors in such a way that you don't mind if they are called on[/color][/color][/color]
                the[color=blue][color=green]
                > > odd[color=darkred]
                > > > > occasion when you weren't expecting it (or not called when you were
                > > > > expecting it).
                > > >
                > > > Hm. I'm not arguing with what you say, but that would essentially[/color][/color][/color]
                mean[color=blue][color=green][color=darkred]
                > > > that the behavior of any C++ program that contains a non-trivial copy
                > > > constructor is undefined. And since most real world C++ programs[/color][/color][/color]
                do,...[color=blue][color=green][color=darkred]
                > > >[/color]
                > >
                > > If the non-trivial copy constructor has a side effect then yes. But most
                > > copy constructors (trivial or not) do nothing other than copy objects.[/color]
                >
                > Are you using "undefined" in the strict, nasal-demon sense of the
                > word? Given this class and global variable:
                >
                > int global = 0;
                >
                > class foo
                > {
                > public:
                > foo(double d) : d_(d) {}
                > foo(const foo& other) : d_(other.d_) { ++global; }
                >
                > private:
                > double d_;
                > };
                >
                > there's no UB in copying foo objects is there? It's just that you've
                > no way of knowing what the value of 'global' will be after an
                > expression in which the copy constructor could be elided.
                >
                > Since I don't write copy constructors with side effects, I am asking
                > purely out of academic interest.
                >
                > --
                > GJD[/color]

                You're right, I think the standard calls this 'unspecified behaviour' not
                undefined behaviour.

                john


                Comment

                Working...