struct Kitty
{
int x;
float y;
double z[2];
};
struct Meow
{
Kitty k1;
Kitty k2;
};
int main()
{
float Meow::*member = &Meow::k2.y;
double Meow::*element = &Meow::k2.z[1];
//...
}
It's not prohibited; you're doing it wrong. `x`, `y`, and `z` are not members of `Meow`; they are members of `Kitty`.
Also, I'm not positive, but I'm fairly sure that you can't get a member pointer to an element of an array.
On Thu, Jun 4, 2015 at 2:35 PM, Myriachan <myri...@gmail.com> wrote:Why is it illegal to acquire a member data pointer to a member of a member? Like:
struct Kitty
{
int x;
float y;
double z[2];
};
struct Meow
{
Kitty k1;
Kitty k2;
};
int main()
{
float Meow::*member = &Meow::k2.y;
double Meow::*element = &Meow::k2.z[1];
//...
}
If a non-static data member of a class is of a class type with virtual bases, that shouldn't matter, because members must be of most-derived type. This applies recursively. Therefore, the location of the virtual base class subobjects should always be known for non-static data members. The only case of virtual base class subobjects to worry about is the top-level class, which member pointers already have to handle.
So why is this prohibited?Because the default state for a language feature is "not present"; this is not a natural consequence of the existing rules, and no-one has proposed adding it.
That said, this sort of thing has been discussed by at least a handful of committee members (but as far as I'm aware, not by EWG). I think there may even have been an early draft of a paper. The idea was to allow . and [] on an expression of pointer-to-member type, to compute a pointer-to-member of the specified subobject of the pointee. (This is a bit more general than what you're suggesting, because it can be done after the pointer-to-member is first formed.) That'd look like this:double Meow::*member = (&Meow::k2).z[1];// orauto x = &Meow::k2;auto y = x.z[1];
There was also a suggestion of allowing unary - on a pointer-to-member, to transform 'T U::*' into 'U T::*' (so you can get back to the surrounding object from a pointer to a subobject), and of allowing .* to combine 'T U::*' and 'U V::*' into 'T V::*'.
struct Inner
{
char c;
};
struct Demo
{
Inner first;
Inner second;
};
int main()
{
Demo Inner::*n = nullptr;
Demo Inner::*a = -&Demo::second;
std::printf("%d\n", n == a);
return 0;
}
On Thursday, June 4, 2015 at 3:03:23 PM UTC-7, Nicol Bolas wrote:It's not prohibited; you're doing it wrong. `x`, `y`, and `z` are not members of `Meow`; they are members of `Kitty`.
Also, I'm not positive, but I'm fairly sure that you can't get a member pointer to an element of an array.
Why is it wrong? I know that it is wrong by the current rules, but I don't see a good argument that it should be wrong.
For example, this in GCC and MSVC in an imagined implementation of unary operator -:
struct Inner
{
char c;
};
struct Demo
{
Inner first;
Inner second;
};
int main()
{
Demo Inner::*n = nullptr;
Demo Inner::*a = -&Demo::second;
std::printf("%d\n", n == a);
return 0;
}
Melissa
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
On 2015–06–05, at 11:59 AM, Nicol Bolas <jmck...@gmail.com> wrote:Define "should". `x`, `y` and `z` are unquestionably not members of `Meow`. Therefore, it is not unreasonable to suggest that they should not be directly accessible via a `Meow::*` member pointer. After all, it is a pointer to a member of `Meow`, and they're not members. This is at least part of the logic behind it.
Another part of that logic starts with a syntactic issue. Namely, how to name members of members of a type, without having an actual instance of that type. `Meow::k1` is the qualified name of that member as a construct. However, using the '.' syntax doesn't mean to get a name; it means to access a member of an object. You don't have an object of type `Meow` or `Kitty` yet, so `Meow::k1.x` is functionally meaningless.
To give it meaning would be to give the '.' operator a new function. It would no longer be an operator; it would become part of the name syntax, but only in certain cases. And that would be... troublesome, syntactically, since it means that the definition of a name needs to change. It'd be more reasonable to use :: notation.
And what about members that are tuples? Why can't you get a "member pointer" to a tuple element?
I can keep going with that, but my point is clear: there are a lot of difficulties surrounding something like that. It's not as simple as you're making it out to be.
I can appreciate the desire to have a pointer to a sub-member. But maybe there's a better way to do what you're trying to do than to use pointers-to-members at all.
On 2015–06–05, at 1:48 PM, David Krauss <pot...@mac.com> wrote:They’re subobjects of Meow. Members of a base subobject are also not member subobjects of a derived class, but pointer-to-members can reach them.
“.” and “::” already behave similarly. Both invoke member name lookup. The LHS of :: names a class (or such), and the LHS of . names an object. It’s not too far out to define . for the case that the LHS is an unbound subobject.Even if it were defined for the case that the LHS is a pointer-to-member type, as Richard mentioned, it would certainly still be a built-in operator. Compare to the complexity of overloaded operator->.
struct Base
{
struct Inner
{
int x;
} inner;
int y[2];
};
struct Derived : Base
{
int *Function1() { return &Base::inner.x; }
int *Function2() { return &Base::y[1]; }
};
And what about members that are tuples? Why can't you get a "member pointer" to a tuple element?This could easily be done in the library: std::get_ptm< 5, decltype (my_tuple) >(). Probably already doable in most current tuple implementations, which store objects in members of base classes.
PTMs are nice, but fickle, like many micro-optimizations are. There’s no reason they shouldn’t get incremental improvements. They’re not an anti-pattern or anything, just a little awkward. Certainly they come in handy for reflection. However… it’s hard to love them.
On Thursday 04 June 2015 22:41:42 Richard Smith wrote:
> > The T U::* becoming U T::* design is a bit problematic from an ABI
> > perspective due to most implementations' representation of null data
> > member
> > pointers as -1.
>
> True. Note that such an approach is already broken:
>
> #include <cassert>
> struct a {};
> struct b : a {};
> struct c : a, b { int n; };
> int b::*p = (int b::*)&c::n;
Is this cast even allowed?
And if it is, what does the standard say about the
validity of reinterpret_casting from one PTM type to another?
struct b has no member n, so I don't see why you should be allowed to access
that member from a b pointer.
> int main() { assert(p != nullptr); }
>
> That assert fires in implementations following the Itanium C++ ABI, even
> though the C++ standard requires it not to. Both issues could be fixed by
> subtracting 1 from negative pointer to data members (or by changing the
> representation of a null pointer to member, but that's a much more
> significant ABI break).
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
On Thursday 04 June 2015 17:01:28 Richard Smith wrote:
> There was also a suggestion of allowing unary - on a pointer-to-member, to
> transform 'T U::*' into 'U T::*' (so you can get back to the surrounding
> object from a pointer to a subobject), and of allowing .* to combine 'T
> U::*' and 'U V::*' into 'T V::*'.
And the addition/subtraction of other PTMs to make more composition.
On Thu, Jun 4, 2015 at 7:00 PM, Myriachan <myri...@gmail.com> wrote:The T U::* becoming U T::* design is a bit problematic from an ABI perspective due to most implementations' representation of null data member pointers as -1.True. Note that such an approach is already broken:#include <cassert>struct a {};struct b : a {};struct c : a, b { int n; };int b::*p = (int b::*)&c::n;int main() { assert(p != nullptr); }That assert fires in implementations following the Itanium C++ ABI, even though the C++ standard requires it not to. Both issues could be fixed by subtracting 1 from negative pointer to data members (or by changing the representation of a null pointer to member, but that's a much more significant ABI break).
#include <cstdio>
#include <cwchar>
struct Base1 { char member1; };
struct Base2 { char member2; };
struct Derived : Base1, Base2 { char member3; };
int main()
{
char Derived::*derivedMember = &Derived::member1;
char Base2::*base2Member = static_cast<char Base2::*>(derivedMember);
if (base2Member == nullptr)
{
std::wprintf(L"Error: base2Member is null!\n");
}
return 0;
}
On 2015–06–05, at 1:41 PM, Richard Smith <ric...@metafoo.co.uk> wrote:That assert fires in implementations following the Itanium C++ ABI, even though the C++ standard requires it not to. Both issues could be fixed by subtracting 1 from negative pointer to data members (or by changing the representation of a null pointer to member, but that's a much more significant ABI break).
On 2015–06–11, at 8:14 AM, David Krauss <pot...@gmail.com> wrote:It seems to require an explicit derived-to-base conversion of pointer-to-char type
If this is well-defined, things get worse than just whether this compares equal to nullptr, because the compiler uses the null check for the idempotency of null member pointers through casting, a lot like null pointers through multiple-inheritance casts. You can get incorrect behavior when casting a pointer as a result.
Melissa
--
On 2015–06–11, at 9:05 AM, Richard Smith <ric...@metafoo.co.uk> wrote:The intent (as I understand it) is that that case is not valid. If it were, 5.2.9/12's "or is a [...] derived class of the class containing the original member" would be redundant. (The rule for the base-to-derived implicit conversions for pointers-to-members is even more broken, because it misses this clause entirely, but the rule is supposed to apply there too.)Essentially, the intended model (as I understand it) is: for a pointer to member for class X, we imagine a hypothetical object A of class X. The pointer to member refers to a direct member of an object B, where either A is B, or A is a base class subobject of B, or B is a base class subobject of A.
On 2015–06–11, at 9:30 AM, David Krauss <pot...@gmail.com> wrote:Also, on the topic of diagnosis and fixing, along with the char T::* case is the PTMF case fn T::* , where fn is a function type matching some nonstatic member function of a non-initial base class whose preceding byte in layout order is occupied by a base of size 1.
On 2015–06–11, at 9:05 AM, Richard Smith <ric...@metafoo.co.uk> wrote:The intent (as I understand it) is that that case is not valid. If it were, 5.2.9/12's "or is a [...] derived class of the class containing the original member" would be redundant. (The rule for the base-to-derived implicit conversions for pointers-to-members is even more broken, because it misses this clause entirely, but the rule is supposed to apply there too.)Essentially, the intended model (as I understand it) is: for a pointer to member for class X, we imagine a hypothetical object A of class X. The pointer to member refers to a direct member of an object B, where either A is B, or A is a base class subobject of B, or B is a base class subobject of A.The definition of the pointer-to-member operators [expr.mptr.oper] §5.5/4 refers to the dynamic type of the LHS, not class X.If there’s not a DR on this already, there certainly should be.
If I were designing an ABI, I’d probably represent PTM nullptr as the most negative value…
dec eax
jo short member_pointer_was_null
On 2015–06–12, at 2:58 AM, Myriachan <myri...@gmail.com> wrote:Do you mean that you think that my case is valid, or that my case is invalid, but for a different reason than what Richard said?
dec eax
jo short member_pointer_was_null