2

如果我有一个称为 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 中检查了其他类似的消息,使用 boost::bind 和 boost::lambda::new_ptr 返回 shared_ptr 构造函数是关闭的,但是如果我尝试应用它的解决方案,我会得到上面提到的模板错误。

如果有帮助,我很乐意提供示例代码和实际错误,但希望上述信息就足够了。我在 GCC 4.6 上使用 boost 1.47.0 以及在 Fedora 15 上使用 4.7 快照。

4

4 回答 4

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>);
}
于 2011-08-24T08:34:03.277 回答
1

这是一个常见的问题。两种类型相关的事实(在您的情况下通过继承)并不意味着具有这两种类型的模板的实例化保持相同的关系。

解决方案是返回 always shared_ptr<Base>,因为它可以同时保存指向Base或任何派生类型的指针,这将在语义上与您当前的版本兼容(即,在两个版本中,调用者都会获得一个(smart)-pointer-toBase

顺便说一句,我会避免shared_ptr从工厂返回,因为您将选择智能指针强加给所有用户。我宁愿返回一个原始指针(用户可以选择,但在某些情况下它很危险)或 aunique_ptr甚至auto_ptr,它们是安全的并且仍然允许用户选择不同的机制(即,如果你的函数返回auto_ptr一个用户仍然可以使用 a shared_ptrby doing shared_ptr<Base> p( f().release() );,而相反是不可能的(由 a 管理的内存shared_ptr不能被释放以在不同的智能指针中使用。

于 2011-08-24T08:34:07.617 回答
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>;
于 2011-08-24T09:36:00.713 回答
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)) {}
};
于 2011-08-24T09:32:38.140 回答