1

I am trying to convert between two classes and avoid a temporary object.

Here is my declaration for Square:

class CSquare
{
public:
    int dimension;
    CSquare(int dimension);

    // Conversion to Rectangle
    operator CRectangle();

    ~CSquare(void);
};

and here is my declaration for Rectangle:

class CRectangle
{
public:
    int length;
    int width;

    CRectangle(int length, int width);

    //Copy Constructor
    CRectangle(const CRectangle& anotherRectangle); 

    ~CRectangle(void);
};

Why does

CSquare aSquare = CSquare(10);
    CRectangle convertedRect = (CRectangle) aSquare;

invoke the copy constructor?

I have a conversion function:

CSquare::operator CRectangle(){
    return CRectangle(CSquare::dimension,CSquare::dimension);
}

but I'm still getting a temporary object.

How do I avoid the temporary object?

mskfisher
  • 3,291
  • 4
  • 35
  • 48
unj2
  • 52,135
  • 87
  • 247
  • 375

8 Answers8

5

You can avoid copy construction by writing a conversion constructor in CRectangle:

CRectangle::CRectangle(const CSquare & sq)
    :length(sq.dimension),
     width(sq.dimension)
{}
...
...
CSquare aSquare(10);
CRectangle convertedRect(aSquare);
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • 4
    And yet another, better way would be to not define Rectangle and Square differently. Squareness is a predicate that operates on rectangles. If a rectangle has all sides equal then it is square. – Edward Strange Feb 01 '11 at 17:02
  • @Noah: Yes, you could define a function for Rectangle to test that, but that doesn't imply that you should not define a Square class, if one is warranted. – Benjamin Lindley Feb 01 '11 at 17:09
  • while we are it, perhaps that recommending `explicit` in front of `CRectangle` would make sense :) ? – Matthieu M. Feb 01 '11 at 18:46
2

Because you're copying a Rectangle.

I honestly don't know what else to say. Compilers are free to optimize away the call in the case you're showing, but they don't have to. Apparently yours is not.

Edit....

As to how to avoid it, you really can't. You could try to crank up the optimizations but it can be extremely difficult to see if you've succeeded, especially with such a small, trivial construct. If I where you I wouldn't be so worried about it unless I really found, by profiling my code, that an inordinate amount of time was being spent in that function. Then I'd be looking for ways to crunch the algorithms using the code to reduce copies.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
1

How do I avoid the temporary object?

You don't. That's how conversions work. The temporary may be elided (constructing directly into the destination), but that is not required.

It sounds like you want move semantics, which are in C++0x. Even then, the temporary "exists", but it can be moved instead of copied, which, for types where it matters, is often much more efficient.

Is this square/rectangle case not really the situation you care about and only used here for an example?

mskfisher
  • 3,291
  • 4
  • 35
  • 48
Fred Nurk
  • 13,952
  • 4
  • 37
  • 63
0

A temporary CRectangle object is being created, then copied to convertedRect.

nullforce
  • 1,051
  • 1
  • 8
  • 13
0

I would assume since operator CRectangle returns an instance of CRectangle, the copy constructor is invoked for the return value.

James
  • 5,355
  • 2
  • 18
  • 30
  • No. The constructor is invoked when they use initialization syntax to create a copy of that return in a local object. – Edward Strange Feb 01 '11 at 16:40
  • @NoahRoberts: That is true, but the copy ctor may also be used for the return value. In fact, temporaries in this case can be copied many times, or none with RVO. – Fred Nurk Feb 01 '11 at 16:45
0

In general this depends on the compiler. Return value optimization is done by most compiler. So instead of creating a temporary object and assign it to your object the copy Ctor from your object is called.

Also take a look here Copy constructor vs. return value optimization

Community
  • 1
  • 1
mkaes
  • 13,781
  • 10
  • 52
  • 72
0

I can't tell which type of object you're saying has a temporary.

If the square, then you need to remove any excuse for the compiler to create one:

CSquare aSquare(10);

If the rectangle, then you shoudl use a converting constructor instead of an operator, as suggested by @PigBen:

Rectangle::CRectangle(const CSquare & sq)
    :length(sq.dimension),
     width(sq.dimension)
{}
// And then:
CRectangle convertedRect(aSquare);
Mark B
  • 95,107
  • 10
  • 109
  • 188
0

In this particular case, you do it by not having the conversion function and instead use inheritance. E.g.:

class CRectangle
{
public:
    int length;
    int width;

    CRectangle(int length, int width);
};

class CSquare : public CRectangle
{
public:
    CSquare(int dimension) : length(dimension), width(dimension)
    {}
};
Zac Howland
  • 15,777
  • 1
  • 26
  • 42