Refresh your C++ knowledge: pointer-to-member type
Even I have been writing C++ for over 10 years, I still miss a lot of basic syntax of C++. Today, I am about to cover pointer-to-member operator. See the standard expr.mptr.oper. It is hard to see what is going on with .* and ->* without any examples. Fortunately, cppconference has quite a lot of examples.
1
2
3
4
5
6
7
8
9
10
struct C { int m; };
int main() {
int C::* p = &C::m; // pointer to data member m of class C
C c = {7};
std::cout << c.*p << '\n'; // prints 7
C* cp = &c;
cp->m = 10;
std::cout << cp->*p << '\n'; // prints 10
}
See the c.*p and cp->*p above. Tbh, this is my first time seeing a pointer-to-member type int C::*. If you take a few seconds to think about this syntax, then it makes total sense, right? A regular pointer is declared as int *, now with class members, we prefix * with C::.
Similar syntax exists for member function as well. Check the examples in the link above.
How could this be useful? Claude tells me it is useful for generic programming.
1
2
3
4
5
6
7
8
9
10
11
struct Person {
std::string name;
int age;
};
void print(const Person& p, std::string Person::* field) {
std::cout << p.*field << '\n';
}
Person person{"Alice", 30};
print(person, &Person::name); // prints "Alice"
Tbh, I do not think anyone will write code this way.
Member function has no implicit convention
The most common implicit convention in cpp is array-to-pointer and function-to-pointer convention. LLVM semantic analysis has dedicated function for this step DefaultFunctionArrayConversion.
But if you read the footnote of standard, it says
This conversion never applies to non-static member functions because an lvalue that refers to a non-static member function cannot be obtained.
That is why in the above example code, you need to write &C::m, but not C::m. This rule sometimes creates unnecessary confusions. For example,
1
2
3
4
5
6
7
8
9
10
11
12
void free_func(int x) { }
auto f1 = std::bind(free_func, 1); // OK - implicit conversion
auto f2 = std::bind(&free_func, 1); // Also OK - explicit address
struct Obj {
void member_func(int x) { }
};
Obj obj;
auto mf1 = std::bind(&Obj::member_func, &obj, 1); // Must use &
Who to blame? Not C++. It is C’s fault. C++ has no choice but to make it compatible. This stackoverflow post has a good point why this implicit convention is bad. Without decaying member function to a lvalue, code like if (MyClass::MyFunc) won’t compile.