本文介绍了当操作员&操作员时,我如何可靠地获得对象的地址。是重载?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下程序:

  struct ghost 
{
// ghosts喜欢假装它们不存在
ghost * operator&()const volatile {return 0; }
};

int main()
{
ghost clyde;
ghost * clydes_address =& clyde; // darn;这不是clyde的地址:'(
}

如何获得 clyde 的地址?



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



我知道C ++ 11的 std :: addressof 函数模板,但我不想在这里使用它:我想了解一个标准库实现者如何实现这个函数模板。在C ++ 11中,可以使用 std:/ p>

解决方案

Update:而不是 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)));
}

静态内联T * f(T * v,int){return v; }
};

template< class T>
T * addressof(T& v){
return addressof_impl< T> :: f(addr_impl_ref< T&
}



如果转换运算符产生 T * ,那么我们有一个模糊性:for f(T&,long)第二个参数需要促销,而 f(T *,int)在第一个(感谢@litb)调用转换操作符



这是当 addr_impl_ref 开始时。C ++标准要求转换序列最多只能包含一个用户定义转换。通过在 addr_impl_ref 中包装类型并强制使用转换序列,我们禁用该类型所带的任何转换操作符。



因此选择 f(T&,long)重载(并执行Integral Promotion)。


$ b


因此,选择重载,因为类型与 T * 参数不匹配。



注意:从文件中有关Borland兼容性的评论中,数组不会衰减到指针,而是通过引用传递。

我们想避免应用<$ c $



标准保证 reinterpret_cast

code>可以用于这项工作(见@Matteo Italia的回答:5.2.10 / 10)。



Boost增加了一些细节, const volatile 限定符以避免编译器警告(并正确使用 const_cast




  • T& 转换为 volatile&

  • 取消 const volatile

  • 应用& 运算符接受地址

  • code> T *



const / volatile juggling有一点黑魔法,但它确实简化了工作(而不是提供4个重载)。注意,由于 T 是不合格的,如果我们通过 ghost const& ,那么 T * ghost const * ,因此限定符没有真正丢失。



strong> EDIT:指针重载用于指向函数的指针,我修改了上面的解释。我仍然不明白为什么这是必要的



以下有点总结。


Consider the following program:

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 :'(
}

How do I get clyde's address?

I'm looking for a solution that will work equally well for all types of objects. A C++03 solution would be nice, but I'm interested in C++11 solutions too. If possible, let's avoid any implementation-specific behavior.

I am aware of C++11's std::addressof function template, but am not interested in using it here: I'd like to understand how a Standard Library implementor might implement this function template.

解决方案

Update: in C++11, one may use std::addressof instead of boost::addressof.


Let us first copy the code from Boost, minus the compiler work around bits:

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 );
}

Note: addressof cannot be used with a pointer to function

In C++ if void func(); is declared, then func is a reference to a function taking no argument and returning no result. This reference to a function can be trivially converted into a pointer to function -- from @Konstantin: According to 13.3.3.2 both T & and T * are indistinguishable for functions. The 1st one is an Identity conversion and the 2nd one is Function-to-Pointer conversion both having "Exact Match" rank (13.3.3.1.1 table 9).

The reference to function pass through addr_impl_ref, there is an ambiguity in the overload resolution for the choice of f, which is solved thanks to the dummy argument 0, which is an int first and could be promoted to a long (Integral Conversion).

Thus we simply returns the pointer.

If the conversion operator yields a T* then we have an ambiguity: for f(T&,long) an Integral Promotion is required for the second argument while for f(T*,int) the conversion operator is called on the first (thanks to @litb)

That's when addr_impl_ref kicks in. The C++ Standard mandates that a conversion sequence may contain at most one user-defined conversion. By wrapping the type in addr_impl_ref and forcing the use of a conversion sequence already, we "disable" any conversion operator that the type comes with.

Thus the f(T&,long) overload is selected (and the Integral Promotion performed).

Thus the f(T&,long) overload is selected, because there the type does not match the T* parameter.

Note: from the remarks in the file regarding Borland compatibility, arrays do not decay to pointers, but are passed by reference.

We want to avoid applying operator& to the type, as it may have been overloaded.

The Standard guarantees that reinterpret_cast may be used for this work (see @Matteo Italia's answer: 5.2.10/10).

Boost adds some niceties with const and volatile qualifiers to avoid compiler warnings (and properly use a const_cast to remove them).

  • Cast T& to char const volatile&
  • Strip the const and volatile
  • Apply the & operator to take the address
  • Cast back to a T*

The const/volatile juggling is a bit of black magic, but it does simplify the work (rather than providing 4 overloads). Note that since T is unqualified, if we pass a ghost const&, then T* is ghost const*, thus the qualifiers have not really been lost.

EDIT: the pointer overload is used for pointer to functions, I amended the above explanation somewhat. I still do not understand why it is necessary though.

The following ideone output sums this up, somewhat.

这篇关于当操作员&amp;操作员时,我如何可靠地获得对象的地址。是重载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 10:57