代码之家  ›  专栏  ›  技术社区  ›  Jon Purdy

把一个临时的地址,加上一个曲解

  •  4
  • Jon Purdy  · 技术社区  · 14 年前

    我有一个功能 address_of ,返回 Pointer (封装 shared_ptr )它的论点。 地址地址 需要同时处理lvalues和rvalues,因此有两个版本 地址地址 :一个接受引用,另一个接受右值引用。由于获取临时地址是件坏事,因此 地址地址 需要执行移动构造以便 指针 真正拥有某物。实现非常简单:

    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抱怨说 地址地址 含糊不清:

    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&]
    

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

    1 回复  |  直到 14 年前
        1
  •  3
  •   Edward Strange    14 年前

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

    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。

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