2011-08-24 62 views
2

如果我有一個稱爲基地以及類Derived1和Derived2的從基地繼承多態基類。然後我可以使用boost :: lambda創建一個排序工廠。喜歡的東西:升壓拉姆達與shared_ptr的

typedef boost::function<Base *()> Creator; 
std::map<std::string,Creator> map1; 
map1["Derived1"] = boost::lambda::new_ptr<Derived1>(); 
map1["Derived2"] = boost::lambda::new_ptr<Derived2>(); 

(這不是真正的代碼,我只是想說明這個問題。)

這工作,所以後來我可以使用字符串做在地圖上查找和然後調用lambda函數來實例化該類。都好。

這裏的問題是,它是在原始指針打交道,我更願意使用智能指針(的std :: shared_ptr的)。

所以,如果我改變來自:

typedef boost::function<Base *>() Creator; 

到:

typedef boost::function<std::shared_ptr<Base> >() Creator; 

那麼我就要從這裏卡住。我使用boost ::拉姆達::綁定與升壓::拉姆達結合:: new_ptr試過,但我運氣不好,不能讓過去編譯錯誤。 (巨大的模板相關的錯誤輸出)。

我查看了StackOverflow中的其他類似消息,Using boost::bind and boost::lambda::new_ptr to return a shared_ptr constructor已關閉,但如果嘗試應用其解決方案,則會出現上述模板錯誤。

我很高興能提供的示例代碼和實際的錯誤,如果有幫助,但希望上面的信息就足夠了。我使用升壓1.47.0 GCC上4.6以及4.7快照在Fedora 15

回答

1
class Base { 
public: 
    virtual ~Base() = 0; 
}; 
Base::~Base() {} 

class Derived1 : public Base {}; 
class Derived2 : public Base {}; 

typedef boost::shared_ptr<Base> BasePtr; 

typedef boost::function<BasePtr()> Creator; 
template <typename T> 
Creator MakeFactory() 
{ 
    namespace la = boost::lambda; 
    return la::bind( 
     la::constructor<BasePtr>(), 
     la::bind(la::new_ptr<T>())); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::map<std::string,Creator> map1;  
    map1["Derived1"] = MakeFactory<Derived1>(); 
    map1["Derived2"] = MakeFactory<Derived2>(); 
    BasePtr p1 = map1["Derived1"](); 
    BasePtr p2 = map1["Derived2"](); 

    return 0; 
} 

然而,爲什麼要去的麻煩時,你可以寫:

template <typename T> 
BasePtr MakeFactoryImpl() 
{ 
    return BasePtr(new T()); 
} 
template <typename T> 
Creator MakeFactory() 
{ 
    return Creator(&MakeFactoryImpl<T>); 
} 
1

這是一種常見問題。兩種類型相關的事實(在你的情況下通過繼承)並不意味着具有這兩種類型的模板的實例化保持相同的關係。

的解決方案是恢復總是shared_ptr<Base>,因爲它可以容納兩個指針,以Base或任何派生類型,這將是與當前版本兼容的語義(即在兩個版本呼叫者得到一個(智能)-pointer-到Base

順便說一句,我會避免從工廠返回shared_ptr,因爲你是迫使你的智能指針選擇到所有用戶。我寧願要麼返回原始指針(用戶可以選擇,卻是危險的在某些情況下)或unique_ptr甚至auto_ptr,這是安全的,仍然允許用戶選擇二不同的機制(即如果你的函數返回一個auto_ptr,用戶仍然可以使用shared_ptrshared_ptr<Base> p(f().release());,而相反是不可能的(內存由shared_ptr管理不能發佈在不同的智能指針使用。

+0

這是一個關於強制智能指針實施到用戶的選擇有趣的一點,我沒有考慮到這一點。 –

0

此單觸和骯髒的返回類型的適配器,不僅是轉換返回類型從Derived*Base*好,但任何轉換類型之間。爲了簡單起見,函數對象不帶任何參數,隨着C++ 11個可變參數模板應該很容易添加任意的參數處理。隨時以您希望的任何方式改善這一點。

template <typename ToType> 
class return_type_adapter 
{  
    template <typename toType> 
    class return_type_adapter_impl_base 
    { 
     public: 
     virtual toType call() = 0; 
    }; 

    template <typename toType, typename Func> 
    class return_type_adapter_impl : public return_type_adapter_impl_base<toType> 
    { 
     public: 
     return_type_adapter_impl (Func func) : func(func) {} 
     toType call() { return toType(func()); } 
     private: 
     Func func; 
    }; 

    boost::shared_ptr<return_type_adapter_impl_base<ToType> > impl_base; 

    public: 
    ToType operator()() { return impl_base->call(); } 

    template <typename Func> 
    return_type_adapter (Func func) : 
     impl_base(new return_type_adapter_impl<ToType, Func>(func)) {} 
}; 
0
map1["Derived1"] = boost::lambda::bind(
    boost::lambda::constructor<boost::shared_ptr<Base>>(), 
    boost::lambda::bind(
     boost::lambda::new_ptr<Derived1>())); 
map1["Derived2"] = boost::lambda::bind(
    boost::lambda::constructor<boost::shared_ptr<Base>>(), 
    boost::lambda::bind(
     boost::lambda::new_ptr<Derived2>())); 

但說實話,這是複雜的地方並沒有真正意義的再使用升壓拉姆達的水平。一個更簡單的解決方案:

template<typename DerivedType> 
boost::shared_ptr<Base> makeDerived() { 
    return boost::shared_ptr<Base>(new DerivedType); 
} 
[...] 

    map1["Derived1"] = makeDerived<Derived1>; 
    map1["Derived2"] = makeDerived<Derived2>;