Is a function argument an implicit cast?

Discussion in 'C++' started by DeMarcus, Mar 3, 2010.

  1. DeMarcus

    DeMarcus Guest

    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
     
    DeMarcus, Mar 3, 2010
    #1
    1. Advertising

  2. On 3 mar, 12:48, DeMarcus <> wrote:
    > 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" );
    }

    --
    Michael
     
    Michael Doubez, Mar 3, 2010
    #2
    1. Advertising

  3. DeMarcus

    DeMarcus Guest

    Michael Doubez wrote:
    > On 3 mar, 12:48, DeMarcus <> wrote:
    >> 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" );
    > }
    >
    > --
    > Michael


    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?
     
    DeMarcus, Mar 3, 2010
    #3
  4. On 3 mar, 15:15, DeMarcus <> wrote:
    > Michael Doubez wrote:
    > > On 3 mar, 12:48, DeMarcus <> wrote:
    > >> 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" );
    > > }

    >
    > > --
    > > Michael

    >
    > 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" );

    --
    Michael
     
    Michael Doubez, Mar 3, 2010
    #4
  5. DeMarcus

    DeMarcus Guest

    >
    > 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.
     
    DeMarcus, Mar 3, 2010
    #5
  6. On 3 mar, 19:08, "Leigh Johnston" <> 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.

    --
    Michael
     
    Michael Doubez, Mar 3, 2010
    #6
  7. DeMarcus

    DeMarcus Guest

    Michael Doubez wrote:
    > On 3 mar, 19:08, "Leigh Johnston" <> 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?
     
    DeMarcus, Mar 3, 2010
    #7
  8. DeMarcus wrote:

    > Michael Doubez wrote:
    >> On 3 mar, 19:08, "Leigh Johnston" <> 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.
     
    Michael Tsang, Mar 4, 2010
    #8
  9. On 4 mar, 09:46, Michael Tsang <> wrote:
    > DeMarcus wrote:
    > > Michael Doubez wrote:
    > >> On 3 mar, 19:08, "Leigh Johnston" <> 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.

    --
    Michael
     
    Michael Doubez, Mar 4, 2010
    #9
  10. DeMarcus

    DeMarcus Guest

    Michael Doubez wrote:
    > On 4 mar, 09:46, Michael Tsang <> wrote:
    >> DeMarcus wrote:
    >>> Michael Doubez wrote:
    >>>> On 3 mar, 19:08, "Leigh Johnston" <> 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!
     
    DeMarcus, Mar 4, 2010
    #10
    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. Replies:
    7
    Views:
    446
  2. Calum Grant
    Replies:
    2
    Views:
    381
    Neelesh Bodas
    Dec 7, 2005
  3. Replies:
    2
    Views:
    393
    red floyd
    Nov 26, 2008
  4. Reckoner
    Replies:
    11
    Views:
    742
    Steven D'Aprano
    Jan 19, 2009
  5. AikidoGuy
    Replies:
    11
    Views:
    596
    Seebs
    Nov 21, 2011
Loading...

Share This Page