Pointers to Members question

Discussion in 'C++' started by Ben Thomas, Sep 21, 2007.

  1. Ben Thomas

    Ben Thomas Guest

    Hi,

    Is this code okay and compliant with the standard. As far as I
    understand it is correct but I just want to make sure.

    Thank you,
    Ben.

    ----

    #include <iostream>
    using namespace std;

    class Base {
    public:
    typedef void (Base::*PMethod)(void);
    void Invoke (PMethod p) {(this->*p)();}
    };

    class Derived : public Base {
    public:
    void Method (void) {cout << "Hello World" << endl;}
    };

    int main (void) {
    Derived x;
    // *** Is the static_cast correct here ? Or could this leads
    // to undefined behavior on some platform / compiler ?
    x.Invoke(static_cast<Base::pMethod>(&Derived::Method));
    return 0;
    }
     
    Ben Thomas, Sep 21, 2007
    #1
    1. Advertising

  2. Ben Thomas wrote:
    > Hi,
    >
    > Is this code okay and compliant with the standard. As far as I
    > understand it is correct but I just want to make sure.
    >
    > Thank you,
    > Ben.
    >
    > ----
    >
    > #include <iostream>
    > using namespace std;
    >
    > class Base {
    > public:
    > typedef void (Base::*PMethod)(void);
    > void Invoke (PMethod p) {(this->*p)();}
    > };
    >
    > class Derived : public Base {
    > public:
    > void Method (void) {cout << "Hello World" << endl;}
    > };
    >
    > int main (void) {
    > Derived x;
    > // *** Is the static_cast correct here ? Or could this leads
    > // to undefined behavior on some platform / compiler ?
    > x.Invoke(static_cast<Base::pMethod>(&Derived::Method));
    > return 0;
    > }


    Generally speaking, a member of derived is NOT a member of base,
    and that's why the conversion does not exist, and you have to resort
    to some tricks (like casts) to silence the compiler that complains
    otherwise. The static cast (as I understand it) would be called for
    if you have a pointer to member of 'Base' ('Base::*ptr'), somehow
    it was converted to a pointer to a member of 'Derived' ('Derived::*')
    and then your base wants to call it. Then, since _originally_ the
    pointer *was* to a member of 'Base', you're ok to use 'static_cast'.
    If the pointer *never was* to a member of 'Base', casting is *not*
    the right thing to do. You're correct suspecting that the behaviour
    is undefined.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Sep 21, 2007
    #2
    1. Advertising

  3. Ben Thomas

    Pete Becker Guest

    On 2007-09-21 16:36:43 -0400, "Victor Bazarov" <> said:

    >
    > The static cast (as I understand it) would be called for
    > if you have a pointer to member of 'Base' ('Base::*ptr'), somehow
    > it was converted to a pointer to a member of 'Derived' ('Derived::*')
    > and then your base wants to call it. Then, since _originally_ the
    > pointer *was* to a member of 'Base', you're ok to use 'static_cast'.
    > If the pointer *never was* to a member of 'Base', casting is *not*
    > the right thing to do. You're correct suspecting that the behaviour
    > is undefined.
    >


    More generally, once you cast it, you have to cast it back to its
    original type to get well-defined behavior.

    --
    Pete
    Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
    Standard C++ Library Extensions: a Tutorial and Reference
    (www.petebecker.com/tr1book)
     
    Pete Becker, Sep 21, 2007
    #3
  4. Ben Thomas

    James Kanze Guest

    On Sep 21, 10:36 pm, "Victor Bazarov" <> wrote:
    > Ben Thomas wrote:
    > > Is this code okay and compliant with the standard. As far as I
    > > understand it is correct but I just want to make sure.


    > > #include <iostream>
    > > using namespace std;


    > > class Base {
    > > public:
    > > typedef void (Base::*PMethod)(void);
    > > void Invoke (PMethod p) {(this->*p)();}
    > > };


    > > class Derived : public Base {
    > > public:
    > > void Method (void) {cout << "Hello World" << endl;}
    > > };


    > > int main (void) {
    > > Derived x;
    > > // *** Is the static_cast correct here ? Or could this leads
    > > // to undefined behavior on some platform / compiler ?
    > > x.Invoke(static_cast<Base::pMethod>(&Derived::Method));
    > > return 0;
    > > }


    > Generally speaking, a member of derived is NOT a member of base,
    > and that's why the conversion does not exist, and you have to resort
    > to some tricks (like casts) to silence the compiler that complains
    > otherwise. The static cast (as I understand it) would be called for
    > if you have a pointer to member of 'Base' ('Base::*ptr'), somehow
    > it was converted to a pointer to a member of 'Derived' ('Derived::*')
    > and then your base wants to call it. Then, since _originally_ the
    > pointer *was* to a member of 'Base', you're ok to use 'static_cast'.
    > If the pointer *never was* to a member of 'Base', casting is *not*
    > the right thing to do. You're correct suspecting that the behaviour
    > is undefined.


    I think you've got it backwards. Pointer to member casts work
    the opposite of normal pointer casts. There is an implicit D::*
    to B::* conversion (no cast needed). A static_cast is needed
    for D::* to B::*, however, and the using it is only legal if the
    B it is used with is actually a D. Which is the case above. So
    while the above may be as ugly as sin, it is perfectly legal
    (and was used for callbacks in one GUI library---XViews, I
    think---that was widely used in the past).

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Sep 23, 2007
    #4
  5. James Kanze wrote:
    > On Sep 21, 10:36 pm, "Victor Bazarov" <> wrote:
    >> Ben Thomas wrote:
    >>> Is this code okay and compliant with the standard. As far as I
    >>> understand it is correct but I just want to make sure.

    >
    >>> #include <iostream>
    >>> using namespace std;

    >
    >>> class Base {
    >>> public:
    >>> typedef void (Base::*PMethod)(void);
    >>> void Invoke (PMethod p) {(this->*p)();}
    >>> };

    >
    >>> class Derived : public Base {
    >>> public:
    >>> void Method (void) {cout << "Hello World" << endl;}
    >>> };

    >
    >>> int main (void) {
    >>> Derived x;
    >>> // *** Is the static_cast correct here ? Or could this leads
    >>> // to undefined behavior on some platform / compiler ?
    >>> x.Invoke(static_cast<Base::pMethod>(&Derived::Method));
    >>> return 0;
    >>> }

    >
    >> Generally speaking, a member of derived is NOT a member of base,
    >> and that's why the conversion does not exist, and you have to resort
    >> to some tricks (like casts) to silence the compiler that complains
    >> otherwise. The static cast (as I understand it) would be called for
    >> if you have a pointer to member of 'Base' ('Base::*ptr'), somehow
    >> it was converted to a pointer to a member of 'Derived' ('Derived::*')
    >> and then your base wants to call it. Then, since _originally_ the
    >> pointer *was* to a member of 'Base', you're ok to use 'static_cast'.
    >> If the pointer *never was* to a member of 'Base', casting is *not*
    >> the right thing to do. You're correct suspecting that the behaviour
    >> is undefined.

    >
    > I think you've got it backwards. Pointer to member casts work
    > the opposite of normal pointer casts. There is an implicit D::*
    > to B::* conversion (no cast needed).


    Nope. *You* got it backwards. Every member of 'B' is a member of
    'D' as well (by inheritance), so 'B::*' ==> 'D::*' requires no special
    syntax, its implicit (modulo all the "available" and "unambiguous"
    cruft). [conv.mem]/2.

    For the pointers to *objects*, however, it's reverse: Every 'D' is
    a 'B' (modulo "available" and "unambiguous"), so conversion from 'D*'
    to 'B*' is implicit.

    > A static_cast is needed
    > for D::* to B::*, however, and the using it is only legal if the
    > B it is used with is actually a D. Which is the case above. So
    > while the above may be as ugly as sin, it is perfectly legal
    > (and was used for callbacks in one GUI library---XViews, I
    > think---that was widely used in the past).


    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Sep 24, 2007
    #5
  6. Ben Thomas

    James Kanze Guest

    On Sep 24, 3:59 pm, "Victor Bazarov" <> wrote:
    > James Kanze wrote:
    > > On Sep 21, 10:36 pm, "Victor Bazarov" <> wrote:


    [...]
    > >> Generally speaking, a member of derived is NOT a member of base,
    > >> and that's why the conversion does not exist, and you have to resort
    > >> to some tricks (like casts) to silence the compiler that complains
    > >> otherwise. The static cast (as I understand it) would be called for
    > >> if you have a pointer to member of 'Base' ('Base::*ptr'), somehow
    > >> it was converted to a pointer to a member of 'Derived' ('Derived::*')
    > >> and then your base wants to call it. Then, since _originally_ the
    > >> pointer *was* to a member of 'Base', you're ok to use 'static_cast'.
    > >> If the pointer *never was* to a member of 'Base', casting is *not*
    > >> the right thing to do. You're correct suspecting that the behaviour
    > >> is undefined.


    > > I think you've got it backwards. Pointer to member casts work
    > > the opposite of normal pointer casts. There is an implicit D::*
    > > to B::* conversion (no cast needed).


    > Nope. *You* got it backwards. Every member of 'B' is a member of
    > 'D' as well (by inheritance), so 'B::*' ==> 'D::*' requires no special
    > syntax, its implicit (modulo all the "available" and "unambiguous"
    > cruft). [conv.mem]/2.


    > For the pointers to *objects*, however, it's reverse: Every 'D' is
    > a 'B' (modulo "available" and "unambiguous"), so conversion from 'D*'
    > to 'B*' is implicit.


    Yes. There's a mix of misunderstanding what you wrote, and
    mistyping what I wanted to say, there. What I wanted to say was
    that the implicit conversions go in the opposite sense for
    pointers to members, compared to pointers to objects (which you
    just said in a lot clearer form), but that just like pointers to
    objects, you can static_cast in the opposite sense of the
    implicit conversion, *provided* the actual type is really a
    derived; i.e. Base* -> Derived* is legal if the object pointed
    to by Base* is actually a Derived, and Derived::* to Base::* is
    legal if the resulting pointer to member is only used with the
    address of a Base which is actually a Derived.

    So you were wrong concerning undefined behavior. But to be
    quite frank, I also thought it was undefined until I ran into a
    large code base which used it.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Sep 25, 2007
    #6
  7. Ben Thomas

    James Kanze Guest

    On Sep 21, 11:22 pm, Pete Becker <> wrote:
    > On 2007-09-21 16:36:43 -0400, "Victor Bazarov" <> said:
    > > The static cast (as I understand it) would be called for
    > > if you have a pointer to member of 'Base' ('Base::*ptr'), somehow
    > > it was converted to a pointer to a member of 'Derived' ('Derived::*')
    > > and then your base wants to call it. Then, since _originally_ the
    > > pointer *was* to a member of 'Base', you're ok to use 'static_cast'.
    > > If the pointer *never was* to a member of 'Base', casting is *not*
    > > the right thing to do. You're correct suspecting that the behaviour
    > > is undefined.


    > More generally, once you cast it, you have to cast it back to its
    > original type to get well-defined behavior.


    Not really. Consider:

    struct Base
    {
    virtual ~Base() {}
    } ;

    struct Derived1 : public Base
    {
    int i ;
    } ;

    struct Derived2 : public Base
    {
    double d ;
    } ;

    int Base::* pmi = static_cast< int Base::* >( &Derived1::i ) ;
    Base* pd1 = new Derived1 ;
    Base* pd2 = new Derived2 ;
    pd1->*pmi = 42 ; // legal and well defined
    pd2->*pmi = 42 ; // undefined behavior.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Sep 25, 2007
    #7
    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. JFCM
    Replies:
    4
    Views:
    5,758
  2. CoolPint
    Replies:
    8
    Views:
    1,002
    Jeff Schwab
    Dec 14, 2003
  3. Dave
    Replies:
    3
    Views:
    371
    tom_usenet
    Aug 10, 2004
  4. hdixon
    Replies:
    3
    Views:
    653
    hdixon
    Jul 9, 2006
  5. cerr

    pointers, pointers, pointers...

    cerr, Apr 7, 2011, in forum: C Programming
    Replies:
    12
    Views:
    696
Loading...

Share This Page