Sign in to follow this  
popsoftheyear

C++ constructor question

Recommended Posts

So we have this class "SimpleImage" that uses copy-on-write semantics. In a couple places in code, there is this: SimpleImage safeCopy = SimpleImage(some3rdPartyHandle); Now the thought is that this should take an image from a 3rd party library, and attach it to a temporary SimpleImage class. Then, upon construction of "safeCopy", we should get a unique copy because we can't reliably track reference count on images from 3rd parties. Point being, we want a unique copy of the image data. This would work... if it created a temporary SimpleImage object! Instead it is exactly (and I checked the assembly - and this is in debug build!) the equivalent of writing SimpleImage safeCopy(some3rdPartyHandle); which only attaches it and we don't get our unique copy. It doesn't even bother to build our temporary object at all! We have to do this instead: SimpleImage tempCopy(some3rdPartyHandle); SimpleImage safeCopy(tempCopy); or this will work too SimpleImage safeCopy; safeCopy = SimpleImage(some3rdPartyHandle); My question though is why? This is in MSVC2008 - is this correct c++ or a mistake in the compiler? That is.. the fact that it skips making the temporary altogether even in debug build?

Share this post


Link to post
Share on other sites
This goes into the same question. The gist of it is that

T x = a;

and

T x(a);

are mostly equivalent, except when used with explicit constructors, in which case only the latter succeeds.

Share this post


Link to post
Share on other sites
Quote:
Original post by d00fus
This goes into the same question. The gist of it is that

T x = a;

and

T x(a);

are mostly equivalent, except when used with explicit constructors, in which case only the latter succeeds.

My point was that it turned out that

T x = T(a); // or T x(T(a));

and

T x(a);

were coming out to be the same. I had expected the first one to create a temporary T, and it didn't.
Quote:
Original post by jwezorek
Can't you just make a function that returns the copy?

Yup - but I was curious about the not-creating-a-temporary thing just in case it ever matters again in the future.

Share this post


Link to post
Share on other sites
If your copy constructors have normal semantics (not called for side-effects), then it should never matter if they are actually called or not.

With GCC you can force it to not optimize away those copies with -fno-elide-constructors. No idea how to achieve it with VC++.

Share this post


Link to post
Share on other sites
Maybe I'm missing something, but couldn't you just make SimpleImage automatically copy when constructed with a third-party handle? Is there ever a time when you don't want it to do that?

Share this post


Link to post
Share on other sites
Quote:
Original post by theOcelot
Maybe I'm missing something, but couldn't you just make SimpleImage automatically copy when constructed with a third-party handle? Is there ever a time when you don't want it to do that?

For our situation, almost always...

As a side note here's what the problem:
- 20 yr old code
- 3 different image libs, all 3rd party and very old
- Proprietary image lib desired (there is good reason for this)
- How to do all this incrementally?
- Main SimpleImage class wants to use proprietary stuff
- But it can use 3rd party stuff (keyword here is incrementally), but we don't want the performance penalty of making copies of potentially huge images hundreds of times a second. We can't replace code every where at once.
- As we remove dependance on 3rd party stuff and add functionality to proprietary lib, code that uses SimpleImage class already works.

That's it in a nutshell. 3rd party image handles from multiple image libs are scattered all over (in fact many of them are public class members!! And these handles tend to be either integers that mean "something" to the library, or a direct pointer!). So simultaneously we're trying to make the code safer as well - and it's really nasty at places. All the while this all has to be done on the side while we add features according to requirements from boss's boss. As an example, in one class, by replacing 3rd party stuff with our own over the last couple years (plus some other refactoring to a small extent), the line count for the class has gone from 25,000 to just over 10,000.

Share this post


Link to post
Share on other sites
So you need a separate function to create the copy of the data.


SimpleImage x = SimpleImage::copy(handle); //this is a normal thing to do

SimpleImage x = SimpleImage(handle); //being different from
SimpleImage x(handle); //is not normal semantics

Share this post


Link to post
Share on other sites
Hang on, you said that this class uses copy-on-write. But none of the code you posted tries to write to it in any way. Shouldn't that mean that none of your examples make a copy?
Wouldn't you specifically need to call a function that has the exact purpose of performing a deep copy, if you want an actual copy?

Did you go wrong somewhere in your description, or have I understood wrong?

Share this post


Link to post
Share on other sites
Quote:
Original post by popsoftheyear
My point was that it turned out that

T x = T(a); // or T x(T(a));

and

T x(a);

were coming out to be the same. I had expected the first one to create a temporary T, and it didn't.


My understanding of the standard is that

T x = a;

is semantically equivalent to

T x = T(a);

which is likewise equivalent to

T x(T(a));

However, an implementation is free to (and almost certainly will) optimise out the temporaries. The following section of the standard I think specifies this:

Quote:

From section 8.5:

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is an rvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by
constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.


(emphasis mine)

Share this post


Link to post
Share on other sites
Quote:
Original post by popsoftheyear
Quote:
Original post by theOcelot
Maybe I'm missing something, but couldn't you just make SimpleImage automatically copy when constructed with a third-party handle? Is there ever a time when you don't want it to do that?

For our situation, almost always...

As a side note here's what the problem:
- 20 yr old code
- 3 different image libs, all 3rd party and very old


It sounds like you're already wrapping the APIs, though. Isn't the purpose of that wrapping to make it so that you can swap them for ones that behave better? :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this