GCC and copy constructor oddity

Discussion in 'C++' started by Martijn van Buul, Oct 30, 2009.

  1. Hi,

    I stumbled across a strange oddity regarding copy constructors, and I was
    hoping someone could explain where I'm going wrong. I tried searching google,
    and looking through documentation, but couldn't find anything (or I'm blind).

    I combined my test cases in http://www.stack.nl/~martijnb/constructortest.cpp
    but will summarize below.

    Consider a class CBar. Constructor, copy constructor and destructor have
    all been implemented. As expected,

    CBar a;
    CBar *b = new CBar(a);

    results in a call to the default constructor and the copy constructor.

    However:

    CBar *b = new CBar(CBar())

    does *not*. Instead, it only calls the default constructor. Even more
    interesting, the line above *doesn't compile* if I make CBar's copy
    constructor private.

    Can anyone explain me why GCC insists the copy constructor exists, but
    doesn't bother calling it afterwards? I'm using GCC 4.2.1.

    Kind regards,

    Martijn
    --
    Martijn van Buul -
     
    Martijn van Buul, Oct 30, 2009
    #1
    1. Advertising

  2. * Martijn van Buul:
    > Can anyone explain me why GCC insists the copy constructor exists, but
    > doesn't bother calling it afterwards? I'm using GCC 4.2.1.


    I'll answer myself: 12.8.15

    Crap.

    --
    Martijn van Buul -
     
    Martijn van Buul, Oct 30, 2009
    #2
    1. Advertising

  3. Martijn van Buul

    Bas Guest

    "Martijn van Buul" <> wrote in message
    news:...
    >* Martijn van Buul:
    >> Can anyone explain me why GCC insists the copy constructor exists, but
    >> doesn't bother calling it afterwards? I'm using GCC 4.2.1.

    >
    > I'll answer myself: 12.8.15
    >
    > Crap.
    >
    > --
    > Martijn van Buul -


    Well.. to be honest, can you explain WHY then?

    I don't understand either!

    Bas
     
    Bas, Oct 30, 2009
    #3
  4. On Oct 30, 10:47 am, "Bas" <> wrote:
    > "Martijn van Buul" <> wrote in messagenews:...
    >
    > >* Martijn van Buul:
    > >> Can anyone explain me why GCC insists the copy constructor exists, but
    > >> doesn't bother calling it afterwards? I'm using GCC 4.2.1.

    >
    > > I'll answer myself: 12.8.15

    >
    > > Crap.

    >
    > > --
    > > Martijn van Buul -

    >
    > Well.. to be honest, can you explain WHY then?
    >
    > I don't understand either!


    Take this program:

    #include <iostream>
    using namespace std;

    class foo
    {
    public:
    foo() { cout << "foo::foo()" << endl; }
    ~foo() { cout << "foo::~foo()" << endl; }
    foo(foo const& ) { cout << "foo::foo(foo const& )" << endl; }
    foo& operator= (foo ) { cout << "foo& foo::eek:perator= (foo )" <<
    endl; return *this; }
    };

    foo returnByVal()
    {
    foo x;
    return x;
    }

    int main()
    {
    foo x = returnByVal();
    }

    This may have as little as one constructor call and one destructor
    call. It may have as many as one default constructor call, two copy
    constructor calls, and three destructor calls.

    In cases where an object is copy constructed from a temporary, the
    compiler is allowed to remove the temporary, and initialize the object
    using the same arguments that were being used to construct the
    temporary.

    In cases where a function returns by value, the compiler is allowed to
    remove the object in the called function's code which will be
    returned, and instead directly construct the temporary in the caller's
    code.

    These two allowances, when combined, allow a compiler to produce
    efficient return-by-value. It also means you might not be making as
    many copies as you expect. Thus, if you have callable copy
    constructors, your copies better be "equivalent".

    Interesting results from the real world:
    gcc version 4.1.2 20070626
    With "g++ foo.cpp", "g++ -O0 foo.cpp", and "g++ -O3 foo.cpp", the
    output of the program is
    foo::foo()
    foo::~foo()
    gcc always optimizes cases under these allowances. How odd... That
    might make debugging interesting.

    Visual Studios 2008, "standard debug options"
    foo::foo()
    foo::foo(foo const& )
    foo::~foo()
    foo::~foo()
    "standard release options"
    foo::foo()
    foo::~foo()
     
    Joshua Maurice, Oct 30, 2009
    #4
  5. On Oct 30, 1:00 pm, Joshua Maurice <> wrote:
    > In cases where an object is copy constructed from a temporary, the
    > compiler is allowed to remove the temporary, and initialize the object
    > using the same arguments that were being used to construct the
    > temporary.
    >
    > In cases where a function returns by value, the compiler is allowed to
    > remove the object in the called function's code which will be
    > returned, and instead directly construct the temporary in the caller's
    > code.


    Sorry, let me emphasize that even if removing the copy constructor
    would change visible aspects of the program, the compiler is allowed
    to do this. This is not an exception under the "as if" rule. They're
    two explicit exceptions, not governed by the "as if" rule, which can
    change visible aspects of your program.
     
    Joshua Maurice, Oct 30, 2009
    #5
  6. Martijn van Buul

    Guest

    On Oct 31, 1:00 am, Joshua Maurice <> wrote:
    > On Oct 30, 10:47 am, "Bas" <> wrote:
    >
    >
    >
    >
    >
    > > "Martijn van Buul" <> wrote in messagenews:...

    >
    > > >* Martijn van Buul:
    > > >> Can anyone explain me why GCC insists the copy constructor exists, but
    > > >> doesn't bother calling it afterwards? I'm using GCC 4.2.1.

    >
    > > > I'll answer myself: 12.8.15

    >
    > > > Crap.

    >
    > > > --
    > > > Martijn van Buul -

    >
    > > Well.. to be honest, can you explain WHY then?

    >
    > > I don't understand either!

    >
    > Take this program:
    >
    > #include <iostream>
    > using namespace std;
    >
    > class foo
    > {
    > public:
    >   foo() { cout << "foo::foo()" << endl; }
    >   ~foo() { cout << "foo::~foo()" << endl; }
    >   foo(foo const& ) { cout << "foo::foo(foo const& )" << endl; }
    >   foo& operator= (foo ) { cout << "foo& foo::eek:perator= (foo )" <<
    > endl; return *this; }
    >
    > };
    >
    > foo returnByVal()
    > {
    >   foo x;
    >   return x;
    >
    > }
    >
    > int main()
    > {
    >   foo x = returnByVal();
    >
    > }
    >
    > This may have as little as one constructor call and one destructor
    > call. It may have as many as one default constructor call, two copy
    > constructor calls, and three destructor calls.
    >
    > In cases where an object is copy constructed from a temporary, the
    > compiler is allowed to remove the temporary, and initialize the object
    > using the same arguments that were being used to construct the
    > temporary.
    >
    > In cases where a function returns by value, the compiler is allowed to
    > remove the object in the called function's code which will be
    > returned, and instead directly construct the temporary in the caller's
    > code.
    >
    > These two allowances, when combined, allow a compiler to produce
    > efficient return-by-value. It also means you might not be making as
    > many copies as you expect. Thus, if you have callable copy
    > constructors, your copies better be "equivalent".
    >
    > Interesting results from the real world:
    >   gcc version 4.1.2 20070626
    > With "g++ foo.cpp", "g++ -O0 foo.cpp", and "g++ -O3 foo.cpp", the
    > output of the program is
    >   foo::foo()
    >   foo::~foo()
    > gcc always optimizes cases under these allowances. How odd... That
    > might make debugging interesting.
    >
    > Visual Studios 2008, "standard debug options"
    >   foo::foo()
    >   foo::foo(foo const& )
    >   foo::~foo()
    >   foo::~foo()
    > "standard release options"
    >   foo::foo()
    >   foo::~foo()


    If copy constructor is not used?? Why cant we make it private??
     
    , Oct 30, 2009
    #6
  7. On Oct 30, 1:52 pm, ""
    <> wrote:
    > On Oct 31, 1:00 am, Joshua Maurice <> wrote:
    >
    >
    >
    > > On Oct 30, 10:47 am, "Bas" <> wrote:

    >
    > > > "Martijn van Buul" <> wrote in messagenews:...

    >
    > > > >* Martijn van Buul:
    > > > >> Can anyone explain me why GCC insists the copy constructor exists, but
    > > > >> doesn't bother calling it afterwards? I'm using GCC 4.2.1.

    >
    > > > > I'll answer myself: 12.8.15

    >
    > > > > Crap.

    >
    > > > > --
    > > > > Martijn van Buul -

    >
    > > > Well.. to be honest, can you explain WHY then?

    >
    > > > I don't understand either!

    >
    > > Take this program:

    >
    > > #include <iostream>
    > > using namespace std;

    >
    > > class foo
    > > {
    > > public:
    > >   foo() { cout << "foo::foo()" << endl; }
    > >   ~foo() { cout << "foo::~foo()" << endl; }
    > >   foo(foo const& ) { cout << "foo::foo(foo const& )" << endl; }
    > >   foo& operator= (foo ) { cout << "foo& foo::eek:perator= (foo )" <<
    > > endl; return *this; }

    >
    > > };

    >
    > > foo returnByVal()
    > > {
    > >   foo x;
    > >   return x;

    >
    > > }

    >
    > > int main()
    > > {
    > >   foo x = returnByVal();

    >
    > > }

    >
    > > This may have as little as one constructor call and one destructor
    > > call. It may have as many as one default constructor call, two copy
    > > constructor calls, and three destructor calls.

    >
    > > In cases where an object is copy constructed from a temporary, the
    > > compiler is allowed to remove the temporary, and initialize the object
    > > using the same arguments that were being used to construct the
    > > temporary.

    >
    > > In cases where a function returns by value, the compiler is allowed to
    > > remove the object in the called function's code which will be
    > > returned, and instead directly construct the temporary in the caller's
    > > code.

    >
    > > These two allowances, when combined, allow a compiler to produce
    > > efficient return-by-value. It also means you might not be making as
    > > many copies as you expect. Thus, if you have callable copy
    > > constructors, your copies better be "equivalent".

    >
    > > Interesting results from the real world:
    > >   gcc version 4.1.2 20070626
    > > With "g++ foo.cpp", "g++ -O0 foo.cpp", and "g++ -O3 foo.cpp", the
    > > output of the program is
    > >   foo::foo()
    > >   foo::~foo()
    > > gcc always optimizes cases under these allowances. How odd... That
    > > might make debugging interesting.

    >
    > > Visual Studios 2008, "standard debug options"
    > >   foo::foo()
    > >   foo::foo(foo const& )
    > >   foo::~foo()
    > >   foo::~foo()
    > > "standard release options"
    > >   foo::foo()
    > >   foo::~foo()

    >
    > If copy constructor is not used?? Why cant we make it private??


    It might help in the future if you use grammatically correct, whole
    sentences.

    I can only guess you mean to ask "If the compiler will optimize out
    the copy constructor, then the copy constructor will not be used. In
    this situation, my compiler complains that the copy constructor is not
    accessible even though it will not, in fact, be used. Why is this?"

    Formal answer: Because the standard says so. The explicit exception
    which allows the compiler to remove temporaries and copy constructor
    calls specifically says that the compiler must still ensure the the
    copy constructor exists and is accessible.

    I guess a better answer is that the people who wrote the standard did
    not want some piece of code valid on one compiler which elided
    temporaries and copy constructors, and not valid on another compiler
    which did not do this optimization. They preferred that the code would
    either fail on both compilers or pass on both compilers. You know,
    basically help the portability of code.
     
    Joshua Maurice, Oct 30, 2009
    #7
  8. * Joshua Maurice:
    > They preferred that the code would either fail on both compilers or pass on
    > both compilers. You know, basically help the portability of code.


    However, since the outcome on different compilers can now be different should
    your copy constructor have side effects (or if your copy constructor doesn't
    "copy" at all!), portability is down the drain anyway.

    --
    Martijn van Buul -
     
    Martijn van Buul, Oct 31, 2009
    #8
  9. Martijn van Buul

    Bo Persson Guest

    Martijn van Buul wrote:
    > * Joshua Maurice:
    >> They preferred that the code would either fail on both compilers
    >> or pass on both compilers. You know, basically help the
    >> portability of code.

    >
    > However, since the outcome on different compilers can now be
    > different should your copy constructor have side effects (or if
    > your copy constructor doesn't "copy" at all!), portability is down
    > the drain anyway.


    Yes, but if I make the copy constructor private to disable copying, I
    would be really annoyed if my compiler copies it anyway.


    Bo Persson
     
    Bo Persson, Oct 31, 2009
    #9
  10. Martijn van Buul

    Rolf Magnus Guest

    Joshua Maurice wrote:

    > I can only guess you mean to ask "If the compiler will optimize out
    > the copy constructor, then the copy constructor will not be used. In
    > this situation, my compiler complains that the copy constructor is not
    > accessible even though it will not, in fact, be used. Why is this?"
    >
    > Formal answer: Because the standard says so. The explicit exception
    > which allows the compiler to remove temporaries and copy constructor
    > calls specifically says that the compiler must still ensure the the
    > copy constructor exists and is accessible.
    >
    > I guess a better answer is that the people who wrote the standard did
    > not want some piece of code valid on one compiler which elided
    > temporaries and copy constructors, and not valid on another compiler
    > which did not do this optimization. They preferred that the code would
    > either fail on both compilers or pass on both compilers. You know,
    > basically help the portability of code.


    To put it short: The correctness of code does not depend on the optimization
    behavior of your compiler, and so, incorrect code must be marked as such,
    even if it would be optimized away later.
     
    Rolf Magnus, Nov 2, 2009
    #10
  11. Martijn van Buul

    Rolf Magnus Guest

    Martijn van Buul wrote:

    > * Joshua Maurice:
    >> They preferred that the code would either fail on both compilers or pass
    >> on both compilers. You know, basically help the portability of code.

    >
    > However, since the outcome on different compilers can now be different
    > should your copy constructor have side effects (or if your copy
    > constructor doesn't "copy" at all!), portability is down the drain anyway.


    I would say that in a sane program, it doesn't really matter if the object
    is copied or not.
     
    Rolf Magnus, Nov 2, 2009
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Aire
    Replies:
    3
    Views:
    470
    Mike Wahler
    Jan 25, 2004
  2. Allin Cottrell

    gcc prototype oddity

    Allin Cottrell, Jun 23, 2004, in forum: C Programming
    Replies:
    23
    Views:
    4,848
    Dave Thompson
    Jul 8, 2004
  3. ali
    Replies:
    4
    Views:
    581
    David Harmon
    Mar 5, 2007
  4. Generic Usenet Account
    Replies:
    10
    Views:
    2,249
  5. cinsk
    Replies:
    35
    Views:
    2,616
    James Kanze
    Oct 11, 2010
Loading...

Share This Page