Is a function argument an implicit cast?

D

DeMarcus

Hi.

I need to store pointers as void* to be able to check that an
(semi-arbitrary) object cannot be used twice.

It looks like this.

template<typename T>
class BaseClass
{
T t;
};

std::set<const void*> checklist;

template<typename T>
bool testAndSet( const BaseClass<T>& object )
{
// Insert object into set, if already inserted, return false.
return checklist.insert( &object ).second;
}

int main()
{
BaseClass<int> intClass;
assert( testAndSet( intClass ) && "Should not fail" );
assert( testAndSet( intClass ) && "Should fail" );
}

Regarding the conversion to const void* when inserting into the set,
this must be safe, right? Since everything that comes into testAndSet()
are implicitly cast to BaseClass<T> the void* must always be the same
for any single object.

In a previous post Alf P. Steinbach showed the following problem.

struct A
{
int a;
};

struct B : A
{
virtual ~B() {}
int b;
};

int main()
{
B* p1 = new B;
A* p2 = p1;
void* pv1 = p1;
void* pv2 = p2;
assert( pv1 == pv2 && "Fails!" );
}

But isn't my example safe from that since I do an implicit cast in the
function argument, similar to doing the following.

int main()
{
B* p1 = new B;
A* p2 = p1;
void* pv1 = static_cast<A*>(p1);
void* pv2 = static_cast<A*>(p2);
assert( pv1 == pv2 && "Should be ok" );
}

Am I correct about this assumption? Does it say in the standard that two
different pointers to the same object will always be identical after an
implicit cast through a function call like my testAndSet example above?


Thanks,
Daniel
 
M

Michael Doubez

Hi.

I need to store pointers as void* to be able to check that an
(semi-arbitrary) object cannot be used twice.

It looks like this.

template<typename T>
class BaseClass
{
    T t;

};

std::set<const void*> checklist;

template<typename T>
bool testAndSet( const BaseClass<T>& object )
{
    // Insert object into set, if already inserted, return false.
    return checklist.insert( &object ).second;

}

int main()
{
    BaseClass<int> intClass;
    assert( testAndSet( intClass ) && "Should not fail" );
    assert( testAndSet( intClass ) && "Should fail" );

}

Regarding the conversion to const void* when inserting into the set,
this must be safe, right?

Yes. It is safe.
Since everything that comes into testAndSet()
are implicitly cast to BaseClass<T> the void* must always be the same
for any single object.

It will be the same for any single address. This is equivalent to a
reinterpret_cast<void*>().

If you want it to be the same for any single object, you must use a
dynamic_cast<void*>(). You will the have a pointer on the most derived
underlying object.

In a previous post Alf P. Steinbach showed the following problem.

struct A
{
    int a;

};

struct B : A
{
    virtual ~B() {}
    int b;

};

int main()
{
    B* p1 = new B;
    A* p2 = p1;
    void* pv1 = p1;
    void* pv2 = p2;
    assert( pv1 == pv2 && "Fails!" );

}

But isn't my example safe from that since I do an implicit cast in the
function argument, similar to doing the following.

int main()
{
    B* p1 = new B;
    A* p2 = p1;
    void* pv1 = static_cast<A*>(p1);
    void* pv2 = static_cast<A*>(p2);
    assert( pv1 == pv2 && "Should be ok" );

}

Am I correct about this assumption? Does it say in the standard that two
different pointers to the same object will always be identical after an
implicit cast through a function call like my testAndSet example above?

If you are guaranteed that you have only one ancestor, yes but see the
problem:

struct A
{
int a;
};

struct B : A
{
virtual ~B() {}
int b;
};


struct C : A
{
virtual ~C() {}
int c;
};

struct D : B, C
{
virtual ~D() {}
int d;
};


main()
{
C* p1 = new C;
A* p2 = p1;
B* p3 = p1;
void* pv1 = static_cast<A*>(p2);
void* pv2 = static_cast<A*>(p3);
assert( pv1 == pv2 && "Should Fail" );
}
 
D

DeMarcus

Michael said:
Yes. It is safe.


It will be the same for any single address. This is equivalent to a
reinterpret_cast<void*>().

If you want it to be the same for any single object, you must use a
dynamic_cast<void*>(). You will the have a pointer on the most derived
underlying object.



If you are guaranteed that you have only one ancestor, yes but see the
problem:

struct A
{
int a;
};

struct B : A
{
virtual ~B() {}
int b;
};


struct C : A
{
virtual ~C() {}
int c;
};

struct D : B, C
{
virtual ~D() {}
int d;
};


main()
{
C* p1 = new C;
A* p2 = p1;
B* p3 = p1;
void* pv1 = static_cast<A*>(p2);
void* pv2 = static_cast<A*>(p3);
assert( pv1 == pv2 && "Should Fail" );
}

B* p3 = p1; will give a compiler error, which is fine.
I guess you meant the following.

main()
{
D* p1 = new D; // <-- D instead
C* p2 = p1;
B* p3 = p1;
void* pv1 = static_cast<A*>(p2);
void* pv2 = static_cast<A*>(p3);
assert( pv1 == pv2 && "Should Fail" );
}

What does the standard say about such cast?
 
M

Michael Doubez

B* p3 = p1; will give a compiler error, which is fine.
I guess you meant the following.

main()
{
    D* p1 = new D; // <-- D instead
    C* p2 = p1;
    B* p3 = p1;
    void* pv1 = static_cast<A*>(p2);
    void* pv2 = static_cast<A*>(p3);
    assert( pv1 == pv2 && "Should Fail" );

}

Yes, that's what I meant.
What does the standard say about such cast?

pv1 will point to D::C::A of p1 and pv2 will point to D::B::A of p1
(unless you sue virtual inheritance). So you have pv1 != pv2.

I hinted that with dynamic_cast<void*>() you get a pointer on the most
derived class. IMO, this is what you are looking for. In the example:

void* pa1 = static_cast<A*>(p2);
void* pa2 = static_cast<A*>(p3);

void* pv1 = dynamic_cast<void*>(pa1);
void* pv2 = dynamic_cast<void*>(pa2);

assert( pv1 == pv2 && "Should Succeed" );
 
D

DeMarcus

pv1 will point to D::C::A of p1 and pv2 will point to D::B::A of p1
(unless you sue virtual inheritance). So you have pv1 != pv2.

I hinted that with dynamic_cast<void*>() you get a pointer on the most
derived class. IMO, this is what you are looking for. In the example:

void* pa1 = static_cast<A*>(p2);
void* pa2 = static_cast<A*>(p3);

void* pv1 = dynamic_cast<void*>(pa1);
void* pv2 = dynamic_cast<void*>(pa2);

assert( pv1 == pv2 && "Should Succeed" );

Yes, that's true. However, in case I do not have a diamond shaped
non-virtual multiple inheritance as the example above, where in the
standard can I find that a static_cast or implicit function cast always
will yield the same pointer for an arbitrary class hierarchy? I want to
put that as a comment in the code.

I tried to browse the standard to find it but if someone knows where to
look I would appreciate it a lot.
 
M

Michael Doubez

This won't work as dynamic_cast requires polymorphic types, pa1 and pa2 are
void* pointers (not polymorphic), if you meant pa1 and pa2 to be A* pointers

Yes, that's what I meant.
it still won't work as A is not polymorphic.

What do you mean ? A doesn't need to be polymorphic for dynamic_cast<>
to work.
 
D

DeMarcus

Michael said:
Yes, that's what I meant.


What do you mean ? A doesn't need to be polymorphic for dynamic_cast<>
to work.

Actually, yes. According to the standard, it says nothing about that
demand, but according to my compiler (gcc 4.4.1) it gives an error if A
is not polymorphic.

Something is wrong, I agree. Is it because there is no vtable in A?
 
M

Michael Tsang

DeMarcus said:
Actually, yes. According to the standard, it says nothing about that
demand, but according to my compiler (gcc 4.4.1) it gives an error if A
is not polymorphic.

Something is wrong, I agree. Is it because there is no vtable in A?

The standard does say that to cast from base to derived needs a virtual
function in base. (ISO/IEC 14882:2003 Section 5.2.7 Dynamic cast
[expr.dynamic.cast]

1 The result of the expression dynamic_cast<T>(v) (omitted)
2 (omitted)
3 If the type of v is the same as the required result type (omitted), or it
is the same as R except that the class object type in R is move cv-qualified
than the class object type in v, (omitted)
4 (omitted)
5 If T is "pointer to cv1 B" and v has type "pointer to cv2 D" such that B
is a base class of D, (omitted)
6 Otherwise, v shall be a pointer to or an lvalue of a *polymorphic* type.
 
M

Michael Doubez

Actually, yes. According to the standard, it says nothing about that
demand, but according to my compiler (gcc 4.4.1) it gives an error if A
is not polymorphic.
Something is wrong, I agree. Is it because there is no vtable in A?

The standard does say that to cast from base to derived needs a virtual
function in base. (ISO/IEC 14882:2003 Section 5.2.7 Dynamic cast
[expr.dynamic.cast]

1 The result of the expression dynamic_cast<T>(v) (omitted)
2 (omitted)
3 If the type of v is the same as the required result type (omitted), or it
is the same as R except that the class object type in R is move cv-qualified
than the class object type in v, (omitted)
4 (omitted)
5 If T is "pointer to cv1 B" and v has type "pointer to cv2 D" such that B
is a base class of D, (omitted)
6 Otherwise, v shall be a pointer to or an lvalue of a *polymorphic* type.


OK. The RTTI information is not generated if A is not polymorphic (or
without a specific compiler option or pragma) and dynamic_cast<>()
will fail.

Well. Easy enough to fix - use a virtual destructor.
 
D

DeMarcus

Michael said:
DeMarcus said:
Michael Doubez wrote:
I hinted that with dynamic_cast<void*>() you get a pointer on the most
derived class. IMO, this is what you are looking for. In the example:
void* pa1 = static_cast<A*>(p2);
void* pa2 = static_cast<A*>(p3);
void* pv1 = dynamic_cast<void*>(pa1);
void* pv2 = dynamic_cast<void*>(pa2);
assert( pv1 == pv2 && "Should Succeed" );
This won't work as dynamic_cast requires polymorphic types, pa1 and pa2
are void* pointers (not polymorphic), if you meant pa1 and pa2 to be A*
pointers
Yes, that's what I meant.
it still won't work as A is not polymorphic.
What do you mean ? A doesn't need to be polymorphic for dynamic_cast<>
to work.
Actually, yes. According to the standard, it says nothing about that
demand, but according to my compiler (gcc 4.4.1) it gives an error if A
is not polymorphic.
Something is wrong, I agree. Is it because there is no vtable in A?
The standard does say that to cast from base to derived needs a virtual
function in base. (ISO/IEC 14882:2003 Section 5.2.7 Dynamic cast
[expr.dynamic.cast]

1 The result of the expression dynamic_cast<T>(v) (omitted)
2 (omitted)
3 If the type of v is the same as the required result type (omitted), or it
is the same as R except that the class object type in R is move cv-qualified
than the class object type in v, (omitted)
4 (omitted)
5 If T is "pointer to cv1 B" and v has type "pointer to cv2 D" such that B
is a base class of D, (omitted)
6 Otherwise, v shall be a pointer to or an lvalue of a *polymorphic* type.


OK. The RTTI information is not generated if A is not polymorphic (or
without a specific compiler option or pragma) and dynamic_cast<>()
will fail.

Well. Easy enough to fix - use a virtual destructor.

Yes, thanks!
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top