Повысьте лямбду с помощью shared_ptr - PullRequest
2 голосов
/ 24 августа 2011

Если у меня есть полиморфный базовый класс с именем Base, а также классы Derived1 и Derived2, которые наследуются от Base.Затем я могу использовать 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>();

(Это не настоящий код, я просто пытаюсь проиллюстрировать проблему.)

Это работает, поэтому я могу выполнить поиск на картеиспользуя строку, а затем вызвать лямбда-функцию для создания экземпляра этого класса.Все хорошо.

Проблема в том, что он работает с необработанными указателями, я бы предпочел использовать умные указатели (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 близко, но если я пытаюсь применить его решение, я получаю ошибки шаблона, упомянутые выше.

Я рад предоставить пример кода и фактические ошибки, если это поможет, но, надеюсь, приведенной выше информации достаточно.Я использую boost 1.47.0 на GCC 4.6, а также снимок 4.7 на Fedora 15.

Ответы [ 4 ]

1 голос
/ 24 августа 2011

Это распространенная проблема.Тот факт, что два типа связаны (в вашем случае наследованием), не означает, что экземпляры шаблона с этими двумя типами поддерживают одинаковые отношения.

Решение состоит в том, чтобы всегда возвращать shared_ptr<Base>, так какможет содержать оба указателя на Base или любой производный тип, который будет семантически совместим с вашей текущей версией (т. е. в обеих версиях вызывающий объект получает (умный) -поинтер-указатель на Base.

Кроме того, я бы не стал возвращать shared_ptr с фабрики, поскольку вы заставляете свой умный указатель выбирать всем своим пользователям. Я бы предпочел либо вернуть необработанный указатель (пользователь может выбрать, нов некоторых ситуациях это опасно) или unique_ptr или даже auto_ptr, которые безопасны и все еще позволяют пользователю выбирать другой механизм (т. е. если ваша функция возвращает auto_ptr, пользователь все еще может использовать shared_ptr с помощью shared_ptr<Base> p( f().release() );, в то время как обратное невозможно (память, управляемая shared_ptr, не может быть освобождена для использования в другом интеллектуальном пуантет.

1 голос
/ 24 августа 2011
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>);
}
0 голосов
/ 24 августа 2011
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>;
0 голосов
/ 24 августа 2011

Этот быстрый и грязный адаптер типа возврата подходит не только для преобразования типов возврата из 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)) {}
};
...