Type conversion function for user type again.

Z

zaeminkr

I got a good answer here I have still confusing part.

I have two very simple classes

class DRect
{
private :
double x0, y0, x1, y1;
public :
DRect(double a, double b, double c, double d) : x0(a), y0(b),
x1(c), y1(d) {}
void Union(const DRect* p)
{
x0 = MIN(x0, p->x0);
y0 = MIN(y0, p->y0);
x1 = MAX(x1, p->x1);
y1 = MAX(y1, p->y1);
}
};

class IRect
{
private :
int x0, y0, x1, y1;
public :
IRect(int a, int b, int c, int d) : x0(a), y0(b), x1(c), y1(d)
{}
operator DRect() const
{
return DRect(x0, y0, x1, y1);
}
operator DRect*() const
{
return &DRect(x0, y0, x1, y1);
}
};

and the execution code here.

void main()
{
DRect d(5.3, 5.3, 15.6, 15.6);
IRect i(10, 10, 100, 100);

/* 1. No problem with compiling this code. */
d.Union(&(static_cast<DRect>(i)));

/* 2. It isn't compiled. */
d.Union(&i);
}

My question is

1. The second execution code 'd.Union(&i)' is quite resonable. People
usually do something like this.

DRect d1(2.3, 5.3, 98.2, 34.2);
DRect d2(5.2, 4.2, 99.1, 54.9);

d1.Union(&d2);

So I want people can do something like this also.

DRect d1(2.3, 5.3, 98.2, 34.2);
IRect i(5, 4, 99, 54);

d1.Union(&i);

However, the compiler is only accept
'd.Union(&(static_cast<DRect>(i)));' form. My 'operator' functions are
wrong?

2. Where is the temporary object exist? stack?

operator DRect() const
{
return DRect(x0, y0, x1, y1);
}
operator DRect*() const
{
return &DRect(x0, y0, x1, y1);
}

If the temporary DRect object is exist on stack, it's still exist
when the 'DRect::Union' is executed?


Thanks.
 
X

xsws5638

I got a good answer here I have still confusing part.

I have two very simple classes

class DRect
{
private :
double x0, y0, x1, y1;
public :
DRect(double a, double b, double c, double d) : x0(a), y0(b),
x1(c), y1(d) {}
void Union(const DRect* p)
{
x0 = MIN(x0, p->x0);
y0 = MIN(y0, p->y0);
x1 = MAX(x1, p->x1);
y1 = MAX(y1, p->y1);
}

};

class IRect
{
private :
int x0, y0, x1, y1;
public :
IRect(int a, int b, int c, int d) : x0(a), y0(b), x1(c), y1(d)
{}
operator DRect() const
{
return DRect(x0, y0, x1, y1);
}
operator DRect*() const
{
return &DRect(x0, y0, x1, y1);
}

};

and the execution code here.

void main()
{
DRect d(5.3, 5.3, 15.6, 15.6);
IRect i(10, 10, 100, 100);

/* 1. No problem with compiling this code. */
d.Union(&(static_cast<DRect>(i)));

/* 2. It isn't compiled. */
d.Union(&i);

}

My question is

1. The second execution code 'd.Union(&i)' is quite resonable. People
usually do something like this.

DRect d1(2.3, 5.3, 98.2, 34.2);
DRect d2(5.2, 4.2, 99.1, 54.9);

d1.Union(&d2);

So I want people can do something like this also.

DRect d1(2.3, 5.3, 98.2, 34.2);
IRect i(5, 4, 99, 54);

d1.Union(&i);
The auto type conversion conforms to C++ standard. You can refer to
some materials. The conversion only happens in some special situation.
The compiler cannot find all the potential conversion for you.
However, the compiler is only accept
'd.Union(&(static_cast<DRect>(i)));' form. My 'operator' functions are
wrong?

2. Where is the temporary object exist? stack?

operator DRect() const
{
return DRect(x0, y0, x1, y1);
}
operator DRect*() const
{
return &DRect(x0, y0, x1, y1);
}

If the temporary DRect object is exist on stack, it's still exist
when the 'DRect::Union' is executed?
No! when the DRect function return, the temporary obj on stack will be
callbacked.
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

I got a good answer here I have still confusing part.

I have two very simple classes

class DRect
{
private :
double x0, y0, x1, y1;
public :
DRect(double a, double b, double c, double d) : x0(a), y0(b),
x1(c), y1(d) {}
void Union(const DRect* p)
{
x0 = MIN(x0, p->x0);
y0 = MIN(y0, p->y0);
x1 = MAX(x1, p->x1);
y1 = MAX(y1, p->y1);
}

};

class IRect
{
private :
int x0, y0, x1, y1;
public :
IRect(int a, int b, int c, int d) : x0(a), y0(b), x1(c), y1(d)
{}
operator DRect() const
{
return DRect(x0, y0, x1, y1);
}
operator DRect*() const
{
return &DRect(x0, y0, x1, y1);
}

};

and the execution code here.

void main()
{
DRect d(5.3, 5.3, 15.6, 15.6);
IRect i(10, 10, 100, 100);

/* 1. No problem with compiling this code. */
d.Union(&(static_cast<DRect>(i)));

/* 2. It isn't compiled. */
d.Union(&i);

}

Use references instead, pointers should normally not be used unless
you have to.

#include <iostream>

class DRect {
private :
double x0, y0, x1, y1;
public :
DRect(double a, double b, double c, double d)
: x0(a), y0(b), x1(c), y1(d) {}

void Union(const DRect& p) {
std::cout << "Union";
}
};

class IRect {
private :
int x0, y0, x1, y1;
public :
IRect(int a, int b, int c, int d)
: x0(a), y0(b), x1(c), y1(d) {}

operator DRect() const {
return DRect(x0, y0, x1, y1);
}
};

int main() { // Notice int and not void as return-type
DRect d(5.3, 5.3, 15.6, 15.6);
IRect i(10, 10, 100, 100);
d.Union(i);
}
 
J

James Kanze

I got a good answer here I have still confusing part.

I have two very simple classes
class DRect
{
private :
double x0, y0, x1, y1;
public :
DRect(double a, double b, double c, double d) : x0(a), y0(b),
x1(c), y1(d) {}
void Union(const DRect* p)
{
x0 = MIN(x0, p->x0);
y0 = MIN(y0, p->y0);
x1 = MAX(x1, p->x1);
y1 = MAX(y1, p->y1);
}
};
class IRect
{
private :
int x0, y0, x1, y1;
public :
IRect(int a, int b, int c, int d) : x0(a), y0(b), x1(c), y1(d)
{}
operator DRect() const
{
return DRect(x0, y0, x1, y1);
}
operator DRect*() const
{
return &DRect(x0, y0, x1, y1);
}
};
and the execution code here.
void main()

Just a nit, but technically, this shouldn't compile. According
to the language specification, main() must return an int.
{
DRect d(5.3, 5.3, 15.6, 15.6);
IRect i(10, 10, 100, 100);
/* 1. No problem with compiling this code. */
d.Union(&(static_cast<DRect>(i)));

Really? It shouldn't compile---the result of the static cast
here is an rvalue, and you cannot take the address of an rvalue.
/* 2. It isn't compiled. */
d.Union(&i);
}
My question is
1. The second execution code 'd.Union(&i)' is quite resonable.

Not really. DRect and IRect are two classes unrelated by
inheritance. The fact that you can convert one of them to the
other doesn't mean that you can implicitly convert a pointer to
one to a pointer to the other. The relationship here is roughly
the same as that between int and double: an int converts
explicitly to a double, but an int* doesn't convert to a
double*.

The reason for this is, of course, obvious. If you have an int,
take it's address, and try to access it as a double, you're
going to get into deep trouble, very fast.
People usually do something like this.
DRect d1(2.3, 5.3, 98.2, 34.2);
DRect d2(5.2, 4.2, 99.1, 54.9);

So I want people can do something like this also.
DRect d1(2.3, 5.3, 98.2, 34.2);
IRect i(5, 4, 99, 54);
d1.Union(&i);

Why the pointers? This looks like a case where references would
be more appropriate. The langage does allow using an rvalue (a
temporary) to initialize a reference to const.

This might even be a case where inheritance would be
appropriate. I don't know that actual application, but it seems
like you are saying that a user should be able to use an IRect
as a DRect; that is typically what inheritance is about. (On
the other hand, inheritance and assignment don't work very well
together, so if your class has value semantics, you might prefer
keeping the implicit conversions rather than using inheritance.)
However, the compiler is only accept
'd.Union(&(static_cast<DRect>(i)));' form. My 'operator' functions are
wrong?

Curiously enough, g++ only gives a warning here, even with
-std=c++98 -pedantic. Even more surprising, VC++ doesn't even
warn, unless you use /Za (in which case, it is an error). This,
despite the fact that it has never been legal, and that even the
earliest C compilers treated it as an error (so compatibility
with existing code cannot be the reason). It is, at any rate,
definitly illegal, and always has been. (Sun CC rejects it, and
as far as I know, doesn't have an option to allow it.)
2. Where is the temporary object exist? stack?

Where ever the compiler wants to put it. With a couple of
restrictions, however: the resulting code must be reentrant, and
(I think) the compiler may not generate a call to the global
operator new.

In practice, it will be in the same memory space where the
compiler puts local variables---on a machine with a stack,
almost certainly on the stack.
operator DRect() const
{
return DRect(x0, y0, x1, y1);
}
operator DRect*() const
{
return &DRect(x0, y0, x1, y1);
}
If the temporary DRect object is exist on stack, it's still exist
when the 'DRect::Union' is executed?

It exists until the end of the full expression in which it is
constructed. Since the full expression in your case includes
the call to DRect::Union, it will exist during this call. (You
would get into trouble, however, if DRect::Union saved the
pointer somewhere, and used it in a later function.)

Note that while there are different conventions with regards to
when to use references, and when to use pointers, every
convention I've ever heard of would use references in your case:
you don't accept null pointers, you don't modify the referenced
object, and you don't store the pointer for later use, after the
end of the function. And you want to be able to call the
function using temporaries (resulting from arbitrary
expressions or implicit type conversions).
 
Z

zaeminkr

The auto type conversion conforms to C++ standard. You can refer to
some materials. The conversion only happens in some special situation.
The compiler cannot find all the potential conversion for you.

My opinion is different. Compiler knows "DRect::Union(const DRect*)"
function so when it meets 'd.Union(&i)', it knows that the type of 'i'
should be 'DRect' type. However it realizes 'i' is 'IRect' type and
tries to covert them into 'DRect' type.

There are some possible conversions.

1. Convert 'i'(DRect type) into 'const DRect' type and do address
operator(&). It makes 'i' to 'const DRect*' type.
=> needs 'IRect::eek:perator DRect()'

2. Convert '&i'(DRect* type) into 'const DRect*' type.
=> needs 'IRect::eek:perator DRect*()'

I still don't know why compiler doesn't do this.
No! when the DRect function return, the temporary obj on stack will be
callbacked.

No, I don't think so. I do some more with compiler to understand this.

1. When the execution exists in 'main', the 'ESP' and call stack is
like this.

--------------------------------------------
ESP = 0012FEC4

main(int 1, char * * 0x003711c0) line 49
mainCRTStartup() line 206 + 25 bytes
KERNEL32! 7c816fd7()
--------------------------------------------

2. When the execution exists in 'operator DRect', the 'ESP' and call
stack is like this.

--------------------------------------------
ESP = 0012FE68

IRect::eek:perator DRect() line 36
main(int 1, char * * 0x003711c0) line 53 + 12 bytes
mainCRTStartup() line 206 + 25 bytes
KERNEL32! 7c816fd7()
--------------------------------------------

3. Finally, when the execution exists in 'DRect::Union', the 'ESP' and
call stack is like this.

--------------------------------------------
ESP = 0012FE48

DRect::Union(const DRect * 0x0012ff30) line 17
main(int 1, char * * 0x003711c0) line 58
mainCRTStartup() line 206 + 25 bytes
KERNEL32! 7c816fd7()
--------------------------------------------

As you can see, the address of the 'DRect::Union's argument('const
DRect*') is '0x0012ff30'.
It's below 'ESP = 0x0012FE48' which means the temporary DRect instance
is still safe in stack when the execution exists in
'DRect::Union'(Stack grows downward.).

Actually the memory for temporary DRect instance was allocated in
stack when the execution get into 'main' function

The above code, the local var's address is this.

d(local DRect instance) : 0x0012ff60
i(local IRect instance) : 0x0012ff50

*the temporary DRect instance from the type conversion : 0x0012ff30.

Am I right? That's what I understood from debugging.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top