1

Here has been a discussion about the usage of pointer to data member

As far as i know,the pointer to data member like int A::* p does not reserve a real address of that member,but an offset instead. However,i've come across a strange situation as follows:

class TEST
{
 int k;
};
int main()
{
 double TEST::* p;//why?
}

How could the above snippet be compiled without error,since there are actually no double type member of TEST class?(no diagnostic required?) What's more,the cout<<p; could also ran without error,although it may be an undefined behavior.

choxsword
  • 3,187
  • 18
  • 44
  • 1
    What benefit would be gained from complicating the rules to disallow a pointer-to-member for a type that the class doesn't have? – chris Feb 19 '18 at 08:56
  • @chris That is an answer, not a comment. – Martin Bonner supports Monica Feb 19 '18 at 08:57
  • @MartinBonner, I don't see it as an answer, at least how I read the question. I was just curious about the logic. – chris Feb 19 '18 at 08:59
  • @chris maybe to prevent users from negligence of writing wrong code,which is actually of no use.Because `int TEST::*` makes sense,not the `double TEST::*'. – choxsword Feb 19 '18 at 08:59
  • @chris What's more,such a useless pointer need an memory allocation of 4 byte in most 32-bit machines.To be more specific,what about `DoNotExist* p;` ?According to the logic above,this could also be compiled without error,despite the unexsitence of type DoNotExist – choxsword Feb 19 '18 at 09:19
  • @bigxiao, That actually complicates type checking, though. If you take `double TEST::* p` and call `declval().*p`, you get `double`, even though there's no specific member involved. If you do the same with a non-existing type, what do you actually get back? Would two of these give back the same type even though it wasn't declared? It would need more explicit text. You could treat it like an incomplete type, but it isn't. What about the fact that parsing depends on name lookup? `DoNotExist* p;` could be a declaration of `p` or a discarded multiplication and one would have to be picked. – chris Feb 19 '18 at 17:09
  • As for the allocation, seems like a QoI issue. I don't see any reason why the allocation would be necessary, and the compiler should be able to figure out nothing is ever actually _done_ with it. – chris Feb 19 '18 at 17:12

3 Answers3

3

Your sample code:

double TEST::* p;

which declares a pointer-to-double-member is legal despite the fact that TEST doesn't have any double members because there is no benefit to making it illegal. It would add a huge chunk of standardese to the standard for no benefit - you will already get a compiler error if you try to set this to anything other than a null pointer constant.

The code snippet:

double TEST:* p;
std::cout << p << std::endl;

is undefined behaviour (good compilers will warn), for just the same reason that:

int i;
std::cout << i << std::endl

is undefined. In both cases, the variable has no value.

  • 1. maybe to prevent users from negligence of writing wrong code,which is actually of no use.Because `int TEST::* `makes sense,not the `double TEST::* ` ?2. What's more,such a useless pointer need an memory allocation of 4 byte in most 32-bit machines.3. What about `DoNotExist* p;`?According to the logic above,this could also be compiled without error,despite the unexsitence of type DoNotExist – choxsword Feb 19 '18 at 09:16
  • @bigxiao - 1. Who said the code is useless? A subclass may have such a member. So long as the dynamic type of things checks out, it's all perfectly well defined. 2. Again, who said it's useless? The fact you haven't encountered a use for it yet, doesn't make it useless. And I'd suggest you let you compiler worry about "needless allocations". It's perfectly capable of optimizing unused variables out. – StoryTeller - Unslander Monica Feb 19 '18 at 09:35
  • 1
    @bigxiao - And as for 3, `class DoNotExist* p;` in fact does compile with no error. The compiler trusts *you* when you tell it such a class exists. So it also trusts *you* when you tell it create such a pointer to member. So if bothers you so much, be trustworthy to the compiler. – StoryTeller - Unslander Monica Feb 19 '18 at 09:36
  • Actually, `DoNotExist* p` can't be compiled without knowing whether `DoNotExist` is a class/struct, or a typedef for (for example) `char`. Pointers are allowed to be a different size, depending on what they are pointing to. (Although no modern implementation makes use of this latitude.) – Martin Bonner supports Monica Feb 19 '18 at 09:38
2

In C++, an object of type T C::* is allowed to point to a member that actually exists in a base or derived class of C. In other words, code like this is supported:

using BD = double Base::*;

struct Base {
    virtual ~Base() = default;
    int k;
    virtual BD getMember() const = 0;
};

Here, Base doesn't have any members of type double, but it requires its concrete derived classes to implement the getMember function, which, perhaps, returns a pointer to one of its own double members. Such a pointer could later be used to access that member through the base class:

double getValue(const Base& b, BD p) {
    return b.*p;
}

Here, the parameter b could actually refer to an object of a derived class type, and p could point to an appropriate double member of that derived class. If so, this code is perfectly valid and well-defined.

If you want your compiler to warn for unused code or uninitialized variables, there should be command-line options for that.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Are you sure ? Both gcc/clang reject that [code](http://coliru.stacked-crooked.com/a/e8445bb85750e9b0). – Jarod42 Feb 19 '18 at 09:51
  • @jarod42 you have to `static_cast` it. – Brian Bi Feb 19 '18 at 09:53
  • An impressive example but a little confusing for me,since the `double` object is not actually a member of base,and `static_cast` itself has complile time checking. – choxsword Feb 19 '18 at 10:10
  • @bigxiao `static_cast` has a limited amount of compile-time checking. It will allow you to do conversions that can't be checked at compile time, such as downcasts of pointers to classes. Indeed, an upcast of pointers to members is unsafe in the same way as a downcast of pointers to classes. If you want the compiler to stop you from doing unsafe things, don't use casts at all. – Brian Bi Feb 19 '18 at 21:01
0

You might not have definition of the class:

class Test;

double TEST::* p;
Jarod42
  • 203,559
  • 14
  • 181
  • 302