3

我有一个 function address_of,它返回 a Pointer(封装 a shared_ptr)到它的参数。address_of需要同时处理左值和右值,因此有两个版本address_of:一个接受引用,另一个接受右值引用。由于获取临时地址是一件坏事™,因此右值版本address_of需要执行移动构造才能Pointer真正拥有某些东西。实现很简单:

template<class T>
inline Pointer address_of(T& value) {
    return Pointer(&value);
}

template<class T>
inline Pointer address_of(T&& value) {
    return Pointer(new T(std::move(value)));
}

并按预期获取临时作品的地址:

Pointer p = address_of(Derived());

但是当我使用以下代码进行测试时:

Base* object = new Derived();
Pointer p = address_of(*object);

GCC 抱怨调用address_of不明确:

error: call of overloaded ‘address_of(Base&)’ is ambiguous
note: candidates are: Pointer address_of(T&) [with T = Base]
note:                 Pointer address_of(T&&) [with T = Base&]

我的印象是一元*总是返回一个左值,在这种情况下甚至不应该考虑右值版本。这里到底发生了什么?

4

1 回答 1

3

问题是由参考衰减引起的:(正确的术语是“参考崩溃”)

template < typename T >
void f(T && t) { .... }

int x; f(x); // what is f()?

代码中问题的答案是 f() 是:

void f(T& && t) { .... }

由于参考衰减变成这样:

void f(T& t) { .... }

正如您所料,这当然会与任何定义为模棱两可:

template < typename T >
void f(T & t) { .... }

这可能有效(固定版本):

#include <type_traits>
#include <utility>

template < typename T >
struct pointer
{
  pointer(T& t) {}
  pointer(T&& t) {}
};

template < typename T >
pointer<typename std::remove_reference<T>::type> 
address_of(T && t)
{ 
  return pointer<typename std::remove_reference<T>::type>(std::forward<T>(t));
}

int main()
{
  int x = 5;
  pointer<int> o = address_of(x);
  pointer<int> p = address_of(5);
}

原因是这种引用衰减的东西只发生在 T 上模板化的函数中。在这种情况下,您的指针类是,但构造函数本身实际上并不是模板,因此 T& 永远不是 T&& 版本的有效 T。

第一个版本仍然存在与您的代码相同的问题,因为 address_of 只是使用 T 作为指针的模板参数。我们实际上需要原始类型。

于 2010-07-02T16:37:53.780 回答