2016-11-16 113 views
1

我正在閱讀Effective C++ 3rd Edition。在70頁上,作者說:是否將unique_ptr隱式轉換爲支持的原始指針?

Like virtually all smart pointer classes, tr1::shared_ptr and auto_ptr also overload the pointer dereferencing operators (operator-> and operator*), and this allows implicit conversion to the underlying raw pointers (...)

然後,他顯示了shared_ptr一個例子基於一個名爲Investment類特色隱式轉換(這是當時的tr1部分):

shared_ptr<Investment> pi1(); 
bool taxable1 = !(pi1->isTaxFree()); 
        ^implicit conversion 

shared_ptr<Investment> pi2(); 
bool taxable2 = !((*pi2).isTaxFree()); 
        ^implicit conversion 

好,我從那以後用unique_ptr寫了幾個測試用例,他們堅持下來。

我還發現約unique_ptr supporting arraysshared_ptr is also going to(見注)。但是,在我的測試中,隱式轉換似乎不適用於數組周圍的智能指針。

例子:我想這是有效的...

unique_ptr<int[]> test(new int[1]); 

(*test)[0] = 5; 

但它不是,根據我的編譯器(的Visual C++ 2015年更新3)。

然後通過搜索,我發現一些證據表明隱式轉換根本不被支持......比如這個例子:https://herbsutter.com/2012/06/21/reader-qa-why-dont-modern-smart-pointers-implicitly-convert-to

在這一點上,我有疑問。 是否支持(按標準),還是不是?


注:這本書可能是在這個題目有點過時,因爲筆者也第65頁說:「有沒有像auto_ptrtr1::shared_ptr爲dinamically分配的數組,甚至在TR1」。

+0

'*測試[0] = 5;'你的尊重'unique_ptr :: operator []'的結果。當然,它不適用於普通的'int'。 – StoryTeller

+0

@StoryTeller夠公平的。我有固定的,我想...... –

+0

'的unique_ptr '沒有'的operator *()'。 '的unique_ptr 'has'operator *()','同時的unique_ptr ''具有操作符[]()'代替。這樣設定的值,簡單地使用:'測試[0] = 5;' –

回答

4

嗯,這是事情。沒有對基礎指針的隱式轉換,您必須調用特定的get成員函數(它是標準庫中的主題,請考慮std::string::c_str)。

但這是一件好事!隱式轉換指針可以打破unique_ptr的保證。考慮以下幾點:

std::unique_ptr<int> p1(new int); 
std::unique_ptr<int> p2(p1); 

在上面的代碼,編譯器可以嘗試p1小號指針對象傳遞給p2! (它不會因爲這個調用不確定,但假設它不是)。他們都會打電話給delete

但我們仍然想使用智能指針,就好像它是一個原始指針。因此所有的操作符都被重載。


現在讓我們考慮您的代碼:

(*test)[0] = 5; 

它調用unique_ptr::operator*產生的int& 。然後你嘗試使用下標操作符。這是你的錯誤。
如果你有一個std::unique_ptr<int[]>不僅僅是使用operator[]重載手柄提供:

test[0] = 5; 

作爲大衛斯嘉麗指出的那樣,它甚至不應該編譯。陣列版本不應該有這個操作符。

+0

隱式轉換是不錯的,雖然想象實現一個目標C類運行時的界面,用於C++你將不得不像(的NSString :: ALLOC()) - >自動釋放(),的NSString和NSObject的,和NSSpecialString等之間的鑄造需要你訴諸despirate意味着類似於微軟的IUnknown的QueryInterface。我很用我們目前的C++,我們可以自動完成這個過程。 – Dmitry

+1

實際上,只有單個對象的版本,'的unique_ptr ''擁有的operator *()'。陣列版本''unique_ptr ''改爲'operator []()'。因此,而不是返回第一個元素,'(* test)'甚至不應該編譯。 GCC給出了:'錯誤:不對應的「操作*」(數類型是「標準::的unique_ptr 」)' –

+0

@Dmitry,我同意,這是不錯的,這就是爲什麼安德烈Alexandrescu的提供了選擇,因爲在政策「現代C++設計「;使用'明確的'轉換運算符,大部分時間它更不容易出錯。但標準庫堅持更謹慎的態度,我不能真正犯錯。 – StoryTeller

1

作爲說書人表示,隱式轉換會破壞演出,但我想建議考慮這個的另一種方式:

unique_ptrshared_ptr智能指針試圖隱藏底層的原始指針,因爲他們試圖保持一定的所有權語義。如果你想自由地獲得指針並傳遞它,你很容易違反這些語義。他們還提供了一種方法來訪問它(get),因爲它們不能完全阻止你,即使他們想(畢竟你可以按照智能指針,讓指針對象的地址)。但他們仍然想要設置一個障礙,以確保您不會意外訪問它。

所有不雖然輸了!通過定義一種具有非常弱語義的新型智能指針,您仍然可以獲得該語法便利性,從而可以安全地從大多數其他智能指針隱式構造它。考慮:

// ipiwdostbtetci_ptr stands for : 
// I promise I wont delete or store this beyond the expression that created it ptr 
template<class T> 
struct ipiwdostbtetci_ptr { 
    T * _ptr; 
    T & operator*() {return *_ptr;} 
    T * operator->(){return _ptr;} 
    ipiwdostbtetci_ptr(T * raw): _ptr{raw} {} 
    ipiwdostbtetci_ptr(const std::unique_ptr<T> & unq): _ptr{unq.get()} {} 
    ipiwdostbtetci_ptr(const std::shared_ptr<T> & shr): _ptr{shr.get()} {} 
}; 

那麼,這個諷刺命名的智能指針有什麼意義呢?這只是一種指針的多數民衆贊成給予口頭合同的用戶將不會保持它還是活的一個副本之外創建它,並且用戶也不會嘗試刪除它的表達。用戶遵循這些約束(沒有編譯器檢查它),隱式轉換許多智能指針以及任何原始指針都是完全安全的。

現在,您可以實現預期功能的ipiwdostbtetci_ptr(並假設他們會兌現的語義),並方便地稱他們爲:

void f(ipiwdostbtetci_ptr<MyClass>); 
... 
std::unique_ptr<MyClass> p = ... 
f(p); 
相關問題