问题描述
请考虑以下程序:
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::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)));
}
静态内联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
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&
tochar const volatile&
- Strip the
const
andvolatile
- 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.
这篇关于当操作员&操作员时,我如何可靠地获得对象的地址。是重载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!