Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between &C::c and &(C::c)?

Tags:

c++

Test code in below and I put the output info in comment. I was using gcc 4.8.5 and Centos 7.2.

#include <iostream> #include <cstdio>  class C  {     public:         void foo() {             printf("%p, %p\n", &C::c, &(C::c)); // output value is 0x4, 0x7ffc2e7f52e8             std::cout << &C::c << std::endl;    // output value is 1         }         int a;         int c; };  int main(void) {     C co;      printf("%p\n", &C::c);              // output value is 0x4     std::cout << &C::c << std::endl;    // output value is 1  //    printf("%p\n", &(C::c));   // compile error, invalid use of non-static data member 'C::c'      co.foo();      return 0; } 
  1. According to C++ operator Precedence,the :: operator has higher precedence than the & operator. I think &C::c is equal to &(C::c), but the output says otherwise. Why are they different?
  2. &(C::c) causes a compile error in main but not in the foo function,why is that?
  3. The value of &C::c is different in printf and std::cout, why is that?
like image 791
randomeval Avatar asked Oct 23 '18 02:10

randomeval


1 Answers

C++ distinguishes two forms of operands to the & operator, lvalues in general and (qualified) identifiers specifically. In &C::c the operand of & is a qualified identifier (i.e. just a name) whereas in &(C::c) the operand is a general expression (because ( cannot be part of a name).

The qualified identifier form has a special case: If it refers to a non-static member of a class (like your C::c), & returns a special value known as a "pointer to member of C". See here for more information about member pointers.

In &(C::c) there is no special case. C::c is resolved normally and fails because there is no object to get a c member of. At least that's what happens in main; in methods of C (like your foo) there is an implicit this object, so C::c actually means this->c there.

As for why the output is different for printf vs. cout: When you try to print a member pointer with <<, it is implicitly converted to a bool, yielding false if it's a null pointer and true otherwise. false is printed as 0; true is printed as 1. Your member pointer is not null, so you get 1. This is different from normal pointers, which are implicitly converted to void * and printed as addresses, but member pointers cannot be converted to void * so the only applicable overload of operator<< is the one for bool. See https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt#Notes.

Note that technically your printf calls have undefined behavior. %p takes a void * and you're passing it pointers of different types. In normal function calls the automatic conversion from T * to void * would kick in, but printf is a variable-arguments function that provides no type context to its argument list, so you need a manual conversion:

printf("%p\n", static_cast<void *>(&(C::c))); 

The relevant part of the standard is [expr.unary.op], saying:

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 member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C​::​m. Otherwise, if the type of the expression is T, the result has type “pointer to T” [...]

like image 114
melpomene Avatar answered Sep 20 '22 06:09

melpomene