I'm trying to understand the consistency in the error that is thrown in this program:
#include <iostream>
class A{
public:
void test();
int x = 10;
};
void A::test(){
std::cout << x << std::endl; //(1)
std::cout << A::x << std::endl; //(2)
int* p = &x;
//int* q = &A::x; //error: cannot convert 'int A::*' to 'int*' in initialization| //(3)
}
int main(){
const int A::* a = &A::x; //(4)
A b;
b.test();
}
The output is 10 10. I labelled 4 points of the program, but (3) is my biggest concern:
x is fetched normally from inside a member function.x of the object is fetched using the scope operator and an lvalue to the object x is returned.A::x returned an int lvalue in (2), why then does &A::x return not int* but instead returns int A::*? The scope operator even takes precedence before the & operator so A::x should be run first, returning an int lvalue, before the address is taken. i.e. this should be the same as &(A::x) surely? (Adding parentheses does actually work by the way). So why exactly does A::x not return the address of the object x but instead returns the address of the member, ignoring precedence of :: before &?
Basically, it's just that the syntax &A::x (without variation) has been chosen to mean pointer-to-member.
If you write, for example, &(A::x), you will get the plain pointer you expect.
More information on pointers-to-members, including a note about this very property, can be found here.
The C++ standard doesn't explicitly specify operator precedence; it's possible to deduce operator precedence implicitly from the grammar rules, but this approach fails to appreciate the occasional special case like this which doesn't fit into a traditional model of operator precedence.
[expr.unary.op]/3:
The result of the unary
&operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static or variant membermof some classCwith typeT, the result has type 'pointer to member of classCof typeT' and is a prvalue designatingC::m. Otherwise, if the type of the expression isT, the result has type 'pointer toT' and is a prvalue that is the address of the designated object or a pointer to the designated function.
/4:
A pointer to member is only formed when an explicit
&is used and its operand is a qualified-id not enclosed in parentheses. [ Note: that is, the expression&(qualified-id), where the qualified-id is enclosed in parentheses, does not form an expression of type 'pointer to member'.
[expr.prim.general]/9:
A nested-name-specifier that denotes a class, optionally followed by the keyword
template, and then followed by the name of a member of either that class or one of its base classes, is a qualified-id.
What it all adds up to is that an expression of the form &A::x has the type "pointer to member x of class A" if x is a non-static member of a non-union class A, and operator precedence has no influence on this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With