【问题标题】:Boost lambda with shared_ptr使用 shared_ptr 提升 lambda
【发布时间】:2011-08-24 08:16:06
【问题描述】:

如果我有一个称为 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 快照。

【问题讨论】:

    标签: c++ boost-lambda


    【解决方案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>);
    }
    

    【讨论】:

      【解决方案2】:

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

      解决方案是始终返回shared_ptr&lt;Base&gt;,因为它可以保存指向Base 或任何派生类型的指针,这将在语义上与您当前的版本兼容(即,在这两个版本中,调用者都会得到一个 (智能)-指向Base的指针。

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

      【讨论】:

      • 将智能指针实现的选择权强加给用户是一个有趣的观点,我没有考虑过。
      【解决方案3】:

      这个快速而简单的返回类型适配器不仅适用于将返回类型从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)) {}
      };
      

      【讨论】:

        【解决方案4】:
        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>;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-03
          • 1970-01-01
          • 2012-11-25
          • 1970-01-01
          • 1970-01-01
          • 2010-09-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多