如果我有一个名为Base的多态基类,以及继承自Base的Derived1和Derived2类。然后我可以使用boost :: lambda创建一个各种工厂。类似的东西:
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 :: lambda :: bind与boost :: lambda :: new_ptr结合使用,但我没有太多运气,无法解决编译错误。 (大量与模板相关的错误输出。)
我已经检查了StackOverflow中的其他类似消息,Using boost::bind and boost::lambda::new_ptr to return a shared_ptr constructor已关闭,但如果我尝试应用其解决方案,我会收到上面提到的模板错误。
如果有帮助,我很乐意提供示例代码和实际错误,但希望以上信息足够。我在GCC 4.6上使用boost 1.47.0以及在Fedora 15上使用4.7快照。
答案 0 :(得分: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 :(得分:1)
这是一个常见问题。两种类型相关的事实(在您的情况下通过继承)并不意味着具有这两种类型的模板的实例化保持相同的关系。
解决方案是始终返回shared_ptr<Base>
,因为它可以同时保存指向Base
或任何派生类型的指针,这些指针在语义上与您当前的版本兼容(即在两个版本中调用者都获得了(智能) - 指向Base
。
顺便说一句,我会避免从工厂返回shared_ptr
,因为你强迫你选择智能指针进入你的所有用户。我希望返回一个原始指针(用户可以选择,但在某些情况下它是危险的)或unique_ptr
甚至auto_ptr
,它们是安全的并且仍然允许用户选择不同的机制(即如果您的函数返回auto_ptr
,用户仍然可以通过shared_ptr
使用shared_ptr<Base> p( f().release() );
,而相反则不可能(由shared_ptr
管理的内存不能发布以在不同的智能指针中使用。
答案 2 :(得分: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)) {}
};
答案 3 :(得分: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>()));
但老实说,这是复杂程度,使用boost lambda不再有意义。一个更简单的解决方案:
template<typename DerivedType>
boost::shared_ptr<Base> makeDerived() {
return boost::shared_ptr<Base>(new DerivedType);
}
[...]
map1["Derived1"] = makeDerived<Derived1>;
map1["Derived2"] = makeDerived<Derived2>;