C++函数/对象指针获取限制及相关编译问题咨询
C++指针转换与地址获取常见问题解答
测试代码
#include <print> int var {}; void foo() { } struct Bar { char mem; void func() { } }; Bar b {}; int main() { // nullptr std::println( "{}", static_cast<const void*>( nullptr ) ); // pointer to object std::println( "{}", static_cast<const void*>( &var ) ); // pointer to function std::println( "{}", reinterpret_cast<const void*>( &foo ) ); // pointer to member variable std::println( "{}", static_cast<const void*>( &Bar::mem ) ); // doesn't compile std::println( "{}", static_cast<const void*>( &(b.mem) ) ); std::println( "{}", static_cast<const void*>( &(b.*(&Bar::mem)) ) ); // pointer to member function std::println( "{}", reinterpret_cast<const void*>( &Bar::func ) ); // compiles with a warning std::println( "{}", reinterpret_cast<const void*>( &(b.func) ) ); // doesn't compile std::println( "{}", reinterpret_cast<const void*>( &(b.*(&Bar::func)) ) ); // doesn't compile // Did I miss any other category? }
问题1:为何static_cast<const void*>( &Bar::mem )编译失败(&(b.*(&Bar::mem))可正常编译)?
编译器报错:
error: invalid 'static_cast' from type 'char Bar::*' to type 'const void*'
解答
&Bar::mem是成员变量指针,类型为char Bar::*,它不是指向内存地址的普通指针,而是记录了成员在类中的偏移信息,和普通对象指针属于完全不同的类型系统。C++标准明确禁止用static_cast将成员变量指针直接转换为void*。
而&(b.*(&Bar::mem))是先把成员指针&Bar::mem绑定到具体对象b上,得到的是b对象中mem成员的实际内存地址,此时它的类型是char*——普通对象指针,自然可以用static_cast转换成const void*。
问题2:为何无法获取绑定成员函数的地址?
编译器报错:
ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say '&Bar::func'
解答
绑定到对象的成员函数(比如b.func)不是独立的函数实体,C++标准直接禁止获取这种绑定后的成员函数地址。要获取成员函数的指针,必须用&Bar::func的形式,得到的是void (Bar::*)()类型的成员函数指针——这种指针必须和对象实例结合才能调用,不能把绑定后的成员函数当作普通函数取地址。
另外,b.*(&Bar::func)本质上等价于调用b.func(),是函数调用表达式,不是可获取地址的实体,所以也无法取地址。
问题3:哪些指针类型可安全获取(如打印用途)且不触发错误或未定义行为?成员函数指针是否不被允许?
解答
以下类型的指针可以安全转换为const void*用于打印,完全符合标准且无未定义行为:
- nullptr:直接用
static_cast<const void*>(nullptr)转换即可,打印结果通常代表空地址。 - 对象指针:包括普通变量指针(如
&var)、对象成员的实际地址(如&b.mem),这类指针指向具体的内存对象,用static_cast<const void*>转换合法。
其他类型的情况:
- 普通函数指针:可以用
reinterpret_cast<const void*>转换后打印,这属于实现定义行为(不同编译器处理可能有差异),但主流编译器(GCC、Clang、MSVC)都支持,不会触发未定义行为。 - 成员变量指针:不能直接转
void*,但绑定到具体对象后得到的成员实际地址(普通对象指针)可以安全转换。 - 成员函数指针:C++标准不允许将其转换为
void*,哪怕用reinterpret_cast也属于未定义行为,编译器可能给出警告甚至拒绝编译,绝对不要这么做。
你还可以测试数组指针、void*本身等类型,它们都属于普通指针范畴,可安全转换为const void*。
内容的提问来源于stack exchange,提问作者digito_evo
相关产品推荐
相关产品推荐

