2011-08-24 146 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>;