考虑以下程序:
struct ghost { // ghosts like to pretend that they don't exist ghost* operator&() const volatile { return 0; } }; int main() { ghost clyde; ghost* clydes_address = &clyde; // darn; that's not clyde's address :'( }
我如何获得clyde的地址?
clyde
我正在寻找一种对所有类型的对象同样适用的解决方案。C03 解决方案会很好,但我也对 C11 解决方案感兴趣。如果可能,让我们避免任何特定于实现的行为。
我知道 C++11 的std::addressof函数模板,但对在这里使用它不感兴趣:我想了解标准库实现者如何实现这个函数模板。
std::addressof
更新: 在 C++11 中,可以std::addressof使用boost::addressof.
boost::addressof
让我们首先从 Boost 中复制代码,减去编译器对位的处理:
template<class T> struct addr_impl_ref { T & v_; inline addr_impl_ref( T & v ): v_( v ) {} inline operator T& () const { return v_; } private: addr_impl_ref & operator=(const addr_impl_ref &); }; template<class T> struct addressof_impl { static inline T * f( T & v, long ) { return reinterpret_cast<T*>( &const_cast<char&>(reinterpret_cast<const volatile char &>(v))); } static inline T * f( T * v, int ) { return v; } }; template<class T> T * addressof( T & v ) { return addressof_impl<T>::f( addr_impl_ref<T>( v ), 0 ); }
如果我们传递 对函数的引用 会发生什么?
注意:addressof不能与函数指针一起使用
addressof
在 C++ 中,如果void func();声明了,那么func是对不带参数且不返回结果的函数的引用。这种对函数的引用可以简单地转换为指向函数的指针——来自@Konstantin:根据 13.3.3.2 两者T &,并且T *对于函数来说是无法区分的。第一个是身份转换,第二个是函数到指针的转换,两者都具有“精确匹配”等级(13.3.3.1.1 表 9)。
void func();
func
@Konstantin
T &
T *
对函数 pass through的引用addr_impl_ref,在选择的重载决议中存在歧义f,这要归功于 dummy 参数0,这是int第一个并且可以提升为long(积分转换)。
addr_impl_ref
f
0
int
long
因此,我们只需返回指针。
如果我们通过转换运算符传递一个类型会发生什么?
如果转换运算符产生 aT*那么我们有一个歧义:对于f(T&,long)第二个参数需要积分提升,而对于第一个参数f(T*,int)调用转换运算符 (感谢@litb)
T*
f(T&,long)
f(T*,int)
这就是addr_impl_ref开始的时候。C++ 标准要求一个转换序列最多可以包含一个用户定义的转换。通过包装类型addr_impl_ref并强制使用转换序列,我们“禁用”了该类型附带的任何转换运算符。
因此f(T&,long)选择了超载(并执行了整体提升)。
任何其他类型会发生什么?
因此f(T&,long)选择了重载,因为那里的类型与参数不匹配T*。
注意:从文件中关于 Borland 兼容性的说明来看,数组不会衰减为指针,而是通过引用传递。
这种过载会发生什么?
我们希望避免应用于operator&该类型,因为它可能已被重载。
operator&
标准保证reinterpret_cast可用于这项工作(参见@Matteo Italia 的回答:5.2.10/10)。
reinterpret_cast
Boost 添加了一些细节 withconst和volatile限定符以避免编译器警告(并正确使用 aconst_cast来删除它们)。
const
volatile
const_cast
T&
char const volatile&
&
const/volatile杂耍有点黑魔法,但它确实简化了工作(而不是提供 4 个重载)。请注意,since Tis unqualified,如果我们通过 a ghost const&, then T*is ghost const*,因此限定词并没有真正丢失。
T
ghost const&
ghost const*
编辑: 指针重载用于指向函数的指针,我对上面的解释做了一些修改。我仍然不明白为什么它是 必要 的。
以下ideone 输出在某种程度上总结了这一点。