小编典典

当 operator& 重载时,如何可靠地获取对象的地址?

all

考虑以下程序:

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的地址?

我正在寻找一种对所有类型的对象同样适用的解决方案。C03 解决方案会很好,但我也对 C11
解决方案感兴趣。如果可能,让我们避免任何特定于实现的行为。

我知道 C++11 的std::addressof函数模板,但对在这里使用它不感兴趣:我想了解标准库实现者如何实现这个函数模板。


阅读 77

收藏
2022-08-27

共1个答案

小编典典

更新: 在 C++11 中,可以std::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不能与函数指针一起使用

在 C++ 中,如果void func();声明了,那么func是对不带参数且不返回结果的函数的引用。这种对函数的引用可以简单地转换为指向函数的指针——来自@Konstantin:根据
13.3.3.2 两者T &,并且T *对于函数来说是无法区分的。第一个是身份转换,第二个是函数到指针的转换,两者都具有“精确匹配”等级(13.3.3.1.1 表 9)。

对函数 pass through的引用addr_impl_ref,在选择的重载决议中存在歧义f,这要归功于 dummy
参数0,这是int第一个并且可以提升为long(积分转换)。

因此,我们只需返回指针。

如果我们通过转换运算符传递一个类型会发生什么?

如果转换运算符产生 aT*那么我们有一个歧义:对于f(T&,long)第二个参数需要积分提升,而对于第一个参数f(T*,int)调用转换运算符
(感谢@litb)

这就是addr_impl_ref开始的时候。C++
标准要求一个转换序列最多可以包含一个用户定义的转换。通过包装类型addr_impl_ref并强制使用转换序列,我们“禁用”了该类型附带的任何转换运算符。

因此f(T&,long)选择了超载(并执行了整体提升)。

任何其他类型会发生什么?

因此f(T&,long)选择了重载,因为那里的类型与参数不匹配T*

注意:从文件中关于 Borland 兼容性的说明来看,数组不会衰减为指针,而是通过引用传递。

这种过载会发生什么?

我们希望避免应用于operator&该类型,因为它可能已被重载。

标准保证reinterpret_cast可用于这项工作(参见@Matteo Italia 的回答:5.2.10/10)。

Boost 添加了一些细节 withconstvolatile限定符以避免编译器警告(并正确使用 aconst_cast来删除它们)。

  • 投射T&char const volatile&
  • 剥离constvolatile
  • 申请&运营商取地址
  • 回滚到T*

const/volatile杂耍有点黑魔法,但它确实简化了工作(而不是提供 4 个重载)。请注意,since Tis
unqualified,如果我们通过 a ghost const&, then T*is ghost const*,因此限定词并没有真正丢失。

编辑: 指针重载用于指向函数的指针,我对上面的解释做了一些修改。我仍然不明白为什么它是 必要 的。

以下ideone 输出在某种程度上总结了这一点。

2022-08-27