2013-03-28 65 views
6

我想製作帶有共享指針的基類和派生類的重載函數。它似乎適用於引用和原始指針,但不適用於額外派生類情況下的共享指針。使用Visual Studio 2012的時候,但我也得到相同的結果時,我嘗試在這裏http://ideone.com/6uoa0p代碼查看示例代碼:帶共享指針參數歧義的函數重載

#include <memory> 

class Base{}; 
class Derived : public Base {}; 
class ExtraDerived : public Derived {}; 


bool IsBase(Base*){ return true; } 
bool IsBase(Derived*){ return false; } 

bool IsBase(std::shared_ptr<Base>){ return true; } 
bool IsBase(std::shared_ptr<Derived>){ return false; } 

int main() 
{ 
    auto derived = std::make_shared<Derived>(); 
    auto extra_derived = std::make_shared<ExtraDerived>(); 
    // works 
    auto raw_result_derived = IsBase(derived.get()); 
    auto raw_result_extra_derived = IsBase(extra_derived.get()); 
    auto shared_result_derived = IsBase(derived); 
    // doesn't work 
    auto shared_result_extra_derived = IsBase(extra_derived); 
} 

我得到:「對重載函數不明確調用錯誤C2668:‘IsBase’」 。

這看起來不像想要的行爲(因爲它適用於'生''東西)。這是模板的一個限制,是否有另一個原因爲什麼這不起作用或者它是一個錯誤? 我怎樣才能以最醜陋的方式工作?

我能想出的最好的是

//ugly workaround 
bool IsBase(std::shared_ptr<Base>, Base*){ return true; } 
bool IsBase(std::shared_ptr<Derived>, Derived*){ return false; } 
template<typename T> bool IsBase(std::shared_ptr<T> input) 
{ 
    return IsBase(input, input.get()); 
} 
+4

你爲什麼要呢?只需提供基本功能,它就會高興地接受派生類的指針。如果你想捕獲派生類對象,你的方法無論如何不會工作,因爲它需要調用函數參數的動態類型才能正確完成。如果程序員在傳遞之前隱式地將'Ptr '轉換爲'Ptr ',則此過載方法會崩潰。 –

+0

@ JohannesSchaub-litb我沒有想到這種情況。我可能不得不重新考慮我想要的。 – Barabas

回答

4

是這個模板的限制,是有其他原因,這不起作用或者是一個錯誤嗎?

不,這不是一個錯誤。事實上,看起來你已經碰到了智能指針的唯一缺陷:std::shared_ptr<base>可以從std::shared_ptr<derived>以及std::shared_ptr<extra_derived>構建,但是這兩個轉換序列都不比其他的更好(即兩個用戶定義的轉換序列相同長度)。

但是,你仍然可以通過使用一些SFINAE約束解決您的重載:

#include <type_traits> 

// Selected for `std::shared_ptr<Base>` 
template<typename T, typename std::enable_if< 
    std::is_same<T, Base>::value>::type* = nullptr> 
bool IsBase(std::shared_ptr<T>){ return true; } 

// Selected for `std::shared_ptr<T>` where T is a class derived from Base, 
// but not Base itself 
template<typename T, typename std::enable_if< 
    std::is_base_of<Base, T>::value && 
    !std::is_same<T, Base>::value 
    >::type* = nullptr> 
bool IsBase(std::shared_ptr<T>){ return false; } 
+0

可悲的是這不適用於VS2012,但它應該在未來。它適用於這種簡單的情況,但如果我有更多的重載不同的派生類,它可能會成爲一個問題。我不得不考慮我是否真的想要這樣的功能,或者考慮JohannesSchaub-litb對我的問題的評論。 – Barabas