Topic: Why no implicit conversion from "D A::*" to "B A::*"?


Author: fvali@biotrack.com
Date: 1999/01/25
Raw View
In article <bill-2201991010330001@bgibbons.vip.best.com>,
  bill@gibbons.org (Bill Gibbons) wrote:
<snip>
> But the final standard effectively requires the offset implementation and
> disallows the other problem case of casting to a virtual base class.
> So there is no implementation problem, it's just a matter of whether the
> added complexity is justified.
>
> A related issue is the handling of pointers to member arrays:
>
>     struct A {
>         int x[10];
>     };
>     A a;
>
>     int A::(*p1)[10] = &A::x;   // OK
>     (a.*p1)[5];                 // OK

>
>     int A::*p2 = &A::x;         // ill-formed
The above should always be ill-formed.  Just as this is:
int arr[10];
int* pi = &arr; // ill-formed

>     int A::*p3 = (&A::x)[5];    // ill-formed
Something similar to the above should have been well-defined in my op.
What is wrong with defining &A::x[5] as proper syntax?

>     int A::*p4 = p1;            // ill-formed
The above should always be ill-formed.

>     int A::*p5 = p1 + 5;        // ill-formed
The above should always be ill-formed but:
just as we do:
int arr[10];
int (*arr_p) [10] = &arr;
int* pi = &(*arr_p + 5); // alternatively pi = &arr[5]

we could use pointer to members similarly
int A::(*p1)[10] = &A::x;
int A::*p5 = &(*p1 + 5);
But this is getting pretty messy.

Oh by the way does anyone know why the standards committee
decided that parentheses are not
allowed when obtaining the offset that is the pointer to our member.
i.e. &(A::x) is not a pointer to member, why not?


>
> The ill-formed cases are all easy to define and implement.  (The "p3" case
> requires parentheses because "&A::x[5]" is parsed as "&(A::x[5])" which
> does not involve pointers to members.)

I guess, the real question is as you stated: Is this added complexity worth
its utility? Personally I don't think it's that complex especially since the
syntax parallels our use of non-member pointers and arrays. In fact the
asymmetry between pointers to members and pointers to non-members that is
introduced by not defining the above operations, and not defining the
conversion from D A::* to B A::* is a little non-intuitive.

-thanks
-fais


-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: bill@gibbons.org (Bill Gibbons)
Date: 1999/01/25
Raw View
In article <78g8hq$ec6$1@nnrp1.dejanews.com>, fvali@biotrack.com wrote:

> Oh by the way does anyone know why the standards committee
> decided that parentheses are not
> allowed when obtaining the offset that is the pointer to our member.
> i.e. &(A::x) is not a pointer to member, why not?

It's because of the ambiguity in the first case below:

    struct A {
        int x;
    };
    struct B : A {
        void f();
    }

    void B::f() {
        int A::*px = &A::x;  // pointer to member
        int *y = &(A::x);    // same as &this->A::x
    }

Only the exact form "&A::b" (where "A" may be qualified too, e.g. "&A::B::c")
is a pointer to member.  Since pointers to members are not very common, this
helps because there is only one special case to remember.

Compilers which allow you to omit the "&" or the qualifier may fail to compile
standard code, for example:

    struct A {
        int x;
    };
    struct B : A {
        int f();
    }

    void B::f() {
        int y = A::x;  // some compilers may misinterpret as PM
        int *z = &x;   // some compilers may misinterpret as PM
    }

Of course since there is no "bound pointer to member" in C++, allowing
these forms for pointers to member functions is a pure extension
(that is, the corresponding cases for member functions are ill-formed
under the standard) so allowing them is more reasonable.
(Though totally unnecessary, even for MFC.)


-- Bill Gibbons
   bill@gibbons.org


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1999/01/24
Raw View
Your answer is very clear and precise... just one comment

Bill Gibbons wrote:

> Early drafts of the standard permitted an implementation to use a member
> ordinal rather than an offset to represent pointers to data members.

Well, I think that this is less efficient in every case.

And it doesn't support down casts of pointers to members well.

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Alfred Kellner" <alfkellner@magnet.at>
Date: 1999/01/24
Raw View
Bjarke Dahl Ebert <bde@cryptomathic.dk> wrote
>
> According to the new ISO/ANSI standard, there is no implicit conversion from
> type "D A::*" to type "B A::*", even when B is an accessible, unambiguous base
> class of D.
>
> And I think there's even no static_cast either this or the opposite way. A
> reinterpret_cast is definitely not what I'm looking for!
>
> A recent query in comp.lang.c++.moderated reveals, that some compilers do and
> some don't implement this standard. For instance EGCS happily makes that
> implicit conversion.
hmm... egcs-1.1 ...
 assignment to `D (X::*)()' from `B (X::*)()'
 conversion to `D (X::*)()' from `B (X::*)()'
 assignment to `B (X::*)()' from `D (X::*)()'
 conversion to `B (X::*)()' from `D (X::*)()'
>
> What I am asking is if there is a good reason why this conversion is not part of
> the standard.
>
I hope the inline-comments shed some light on the subject.

struct MULTI_INHERITED { short s; };
struct B { long l; };
struct D: MULTI_INHERITED, B { double v; D( const B& ); };
class X {
  D (X::*pmfX_DERV)();
  B (X::*pmfX_base)();
public:
  D get_D();
  B get_B();
  X() : pmfX_DERV( & get_D ), pmfX_base( & get_B ) // ok
  {
    D d; B b;
    d = (this->*pmfX_DERV)(); // 1. straight D=D
    b = (this->*pmfX_DERV)(); // 1. get slice B from D
    b = (this->*pmfX_base)(); // 2. straight B=B
    d = (this->*pmfX_base)(); // 2. construct D from B

    // if the following line would compile
    pmfX_DERV = pmfX_base ; // ERROR
    // then
    d = (this->*pmfX_DERV)(); // 1. straight D=D
    // the call would NOT result in a straight D=D as expected
    // instead the storage provided for a 'return D()' would
    // be initialized with a 'B()' and the D-part is garbage
    // thus garbage would be copied if not worse
    // and
    b = (this->*pmfX_DERV)(); // 1. get slice B from D
    // the storage provided would be interpreted as class D
    // and a imaginary sub-object B would be used which
    // might due to multipl.inheritance NOT in the right place

    // if the following line would compile
    pmfX_base = pmfX_DERV ; // ERROR
    // then
    b = (this->*pmfX_base)(); // 2. straight B=B
    // the storage provided is sizeof(B), but would be
    // initialized with sizeof(D). Data will be destroyed
    // and
    d = (this->*pmfX_base)(); // 2. construct D from B
    // same as above
    }
};
 --ALfred

> Note: This has nothing to do with the conversion "T Base::*" to "T Derived::*"
> or the opposite. On c.l.c++.mod I got many correct answers to this question that
> I was not asking! ;-)
>
Yeah...



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: fvali@biotrack.com
Date: 1999/01/25
Raw View
In article <01be47a4$0cf5da40$15dcdcdc@mentor.magnet.at>,
  "Alfred Kellner" <alfkellner@magnet.at> wrote:
>
<snip>
> I hope the inline-comments shed some light on the subject.
>
> struct MULTI_INHERITED { short s; };
> struct B { long l; };
> struct D: MULTI_INHERITED, B { double v; D( const B& ); };
> class X {
>   D (X::*pmfX_DERV)();
>   B (X::*pmfX_base)();
> public:
>   D get_D();
>   B get_B();
>   X() : pmfX_DERV( & get_D ), pmfX_base( & get_B ) // ok
                    ^^^^^^^^^              ^^^^^^^^^
        Detail: This is not ok. You need &X::get_D and &X::get_B

>   {
>     D d; B b;
>     d = (this->*pmfX_DERV)(); // 1. straight D=D
>     b = (this->*pmfX_DERV)(); // 1. get slice B from D
>     b = (this->*pmfX_base)(); // 2. straight B=B
>     d = (this->*pmfX_base)(); // 2. construct D from B
>
>     // if the following line would compile
>     pmfX_DERV = pmfX_base ; // ERROR
>     // then
>     d = (this->*pmfX_DERV)(); // 1. straight D=D
>     // the call would NOT result in a straight D=D as expected
>     // instead the storage provided for a 'return D()' would
>     // be initialized with a 'B()' and the D-part is garbage
>     // thus garbage would be copied if not worse
>     // and
>     b = (this->*pmfX_DERV)(); // 1. get slice B from D
>     // the storage provided would be interpreted as class D
>     // and a imaginary sub-object B would be used which
>     // might due to multipl.inheritance NOT in the right place
>
>     // if the following line would compile
>     pmfX_base = pmfX_DERV ; // ERROR
>     // then
>     b = (this->*pmfX_base)(); // 2. straight B=B
>     // the storage provided is sizeof(B), but would be
>     // initialized with sizeof(D). Data will be destroyed
>     // and
>     d = (this->*pmfX_base)(); // 2. construct D from B
>     // same as above
>     }
> };
>  --ALfred
>
<snip>
Ok so the result of conversions of pointer to functions or
pointer to member functions is unspecified for the most part,
and your comments illustrate why that should be I guess.


But what about conversions between pointer to data members.


struct B { int i; void f(int) { } };
struct D : B { double v; void f( char ) { } };

struct X { B b; D d; };
int main()
{
  B X::*x_pb = &X::b;
  D X::*x_db = &X::d;
  B X::*x_q = &X::d; // What can go wrong here? store an offset to X::d::B

  X x;
  B b = x.*x_q; // why is this ill-formed, what can go wrong here?
  (x.*x_q).f(0); // ill-formed but should call B::f(int) in X::d
  (x.*x_db).f(0); // ill-formed but should call D::f(char) in X::d
  (x.*x_pb).f(0); // well-formed, calls B::f(0) in X::b
}

-thanks
-fais





-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Bjarke Dahl Ebert <bde@cryptomathic.dk>
Date: 1999/01/22
Raw View
According to the new ISO/ANSI standard, there is no implicit conversion from
type "D A::*" to type "B A::*", even when B is an accessible, unambiguous base
class of D.

And I think there's even no static_cast either this or the opposite way. A
reinterpret_cast is definitely not what I'm looking for!

A recent query in comp.lang.c++.moderated reveals, that some compilers do and
some don't implement this standard. For instance EGCS happily makes that
implicit conversion.

What I am asking is if there is a good reason why this conversion is not part of
the standard.



Note: This has nothing to do with the conversion "T Base::*" to "T Derived::*"
or the opposite. On c.l.c++.mod I got many correct answers to this question that
I was not asking! ;-)

--
Systems Engineer           Cryptomathic A/S
Bjarke Dahl Ebert          Klostergade 28
                           DK-8000    rhus C


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: bill@gibbons.org (Bill Gibbons)
Date: 1999/01/22
Raw View
In article <36A83DB5.965893EC@cryptomathic.dk>, Bjarke Dahl Ebert
<bde@cryptomathic.dk> wrote:

> According to the new ISO/ANSI standard, there is no implicit conversion from
> type "D A::*" to type "B A::*", even when B is an accessible, unambiguous base
> class of D.
>
> And I think there's even no static_cast either this or the opposite way. A
> reinterpret_cast is definitely not what I'm looking for!
>
> A recent query in comp.lang.c++.moderated reveals, that some compilers do and
> some don't implement this standard. For instance EGCS happily makes that
> implicit conversion.
>
> What I am asking is if there is a good reason why this conversion is not
part of
> the standard.

The term "pointer to member" is misleading.  A better name would be
"member selector".

The conversion is not allowed because the result would not refer to an
actual member by its type.

Early drafts of the standard permitted an implementation to use a member
ordinal rather than an offset to represent pointers to data members.
Such an implementation could not handle this kind of cast because it could
not make a multiple inheritance offset adjustment.

But the final standard effectively requires the offset implementation and
disallows the other problem case of casting to a virtual base class.
So there is no implementation problem, it's just a matter of whether the
added complexity is justified.


A related issue is the handling of pointers to member arrays:

    struct A {
        int x[10];
    };
    A a;

    int A::(*p1)[10] = &A::x;   // OK
    (a.*p1)[5];                 // OK

    int A::*p2 = &A::x;         // ill-formed
    int A::*p3 = (&A::x)[5];    // ill-formed
    int A::*p4 = p1;            // ill-formed
    int A::*p5 = p1 + 5;        // ill-formed

The ill-formed cases are all easy to define and implement.  (The "p3" case
requires parentheses because "&A::x[5]" is parsed as "&(A::x[5])" which
does not involve pointers to members.)


How much pointer arithmetic is reasonable on pointers to members?
It depends on how much complexity is reasonable in the language as a whole.


-- Bill Gibbons
   bill@gibbons.org


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Ron Natalie <ron@sensor.com>
Date: 1999/01/22
Raw View
Bill Gibbons wrote:
>
> In article <36A83DB5.965893EC@cryptomathic.dk>, Bjarke Dahl Ebert
> <bde@cryptomathic.dk> wrote:
>
> > According to the new ISO/ANSI standard, there is no implicit conversion from
> > type "D A::*" to type "B A::*", even when B is an accessible, unambiguous base
> > class of D.
> >
>
> >
> > What I am asking is if there is a good reason why this conversion is not
> part of
> > the standard.
>
> The term "pointer to member" is misleading.  A better name would be
> "member selector".
>

Correct, and leaving implementation issues aside.  Think about
the validity of the operation.  All class B members are also
class D members, but it's not the case that all class D members
are class B members.  It therefore is not valid to allow the
D pointer-to-member, which may point to a member that may not
exist in all B objects (B's and things derived from them).

There's even a note in the SPEC (DAMN ANSI PDF PINHEADEDNESS):

The rule for conversion of pointers to members (from pointer
to member to base to pointer to member of derived) appears
inverted compared to the rule for pointers to objects (from
pointer to dervied to pointer to based).  This inversion is
necessary to ensure type safety.

By the way, static_cast should let you cast backwards here
(contrary to the guess by the original poster).



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1999/01/23
Raw View
Ron Natalie wrote:

> Correct, and leaving implementation issues aside.

Please, please, stop answering some other questions
Bjarke Dahl Ebert never asked. Please read the question
before answering it.

Bill has completly answered the question: the implicit
conversion doens't exist because it isn't important and
adding this feature would make the std even larger.

Or in other words: no one cared enough to put it in the
std.

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]