2010-07-02 92 views
4

我有一個功能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&] 

我的印象是一元*總是返回一個左值,在這種情況下甚至不應該考慮右值版本。這裏到底發生了什麼?

回答

2

問題是由參考衰變引起的:(正確的術語是 「參考崩潰」)

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

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

答案代碼中的問題是,f()是:

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

由於參考衰變而變成這樣:

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

正如你所期望的,這當然會是ambiguou s的東西定義爲:

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作爲指針的模板參數,所以第一個版本仍然存在與您的代碼相同的問題。我們實際上需要原始類型。

+0

這很有道理,謝謝。關於如何實現我*做*想要的行爲的任何想法? – 2010-07-02 16:40:47

+0

什麼是參考衰減? – 2010-07-02 16:42:18

+0

@Jon - one。不積極,它會工作,但我把它放在我的答案。 – 2010-07-02 16:46:48