【问题标题】:Taking the Address of a Temporary, with a Twist以一个临时的地址,扭曲
【发布时间】:2010-07-02 16:29:14
【问题描述】:

我有一个函数address_of,它返回一个Pointer(封装一个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&]

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

【问题讨论】:

    标签: c++ c++11 rvalue-reference


    【解决方案1】:

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

    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 作为指针的模板参数。我们实际上需要原始类型。

    【讨论】:

    • 有道理,谢谢。关于如何实现我确实想要的行为的任何想法?
    • @Jon - 一。不肯定它会起作用,但我把它放在我的答案中。
    • @Noah:很接近,但没有雪茄。我得到Pointer&lt;T&gt;::Pointer(T&amp;&amp;) cannot be overloaded with Pointer&lt;T&gt;::Pointer(T&amp;) [with T = Base&amp;]
    • 因为在使用 T 实例化指针 之前忘记删除 T 中的引用。固定。
    • 做到了!我怀疑这会涉及remove_reference,但我被拉走了,没有机会测试它。像魅力一样工作。
    猜你喜欢
    • 2013-03-03
    • 2018-03-14
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多