Невозможно использовать std::future для хранения полиморфного объекта

#c #multithreading #concurrency #future

Вопрос:

 struct Base {
    virtual void squawk () {
        std::cout << " I am base" << std::endl;
    }
};

struct Derived : public Base {
    void squawk () override {
        std::cout << "I am derived" << std::endl;
    }
};

int main () {
    std::future<std::shared_ptr<Base>> f = std::async([](){return std::make_shared<Derived>();});
}
 

Это приводит к следующей ошибке :

 error: conversion from 'future<shared_ptr<Derived>>' to non-scalar type 'future<shared_ptr<Base>>' requested
 

Однако это компилирует :

 std::promise<std::shared_ptr<Base>> p;
std::future<std::shared_ptr<Base>> f = p.get_future();
p.set_value(std::make_shared<Derived>());
 

Не могли бы вы объяснить, пожалуйста, почему? И каков рекомендуемый шаблон для создания фьючерсов для хранения полиморфных объектов?

Ответ №1:

Вы должны явно преобразовать результат make_shared<Derived>() в shared_ptr<Base> :

 std::future<std::shared_ptr<Base>> f = std::async( [](){
    return std::shared_ptr<Base> {std::make_shared<Derived>()};
});

// or

std::future<std::shared_ptr<Base>> f = std::async( []() -> std::shared_ptr<Base> {
    return std::make_shared<Derived>();
});

f.get()->squawk(); // I am derived
 

Ответ №2:

Возвращаемый тип вашей лямбды-a shared_ptr<Derived> . Следовательно, будущее, которое async будет создано, содержит shared_ptr<Derived> А. Если вы хотите, чтобы у него был другой тип, вам нужно сделать возвращаемый тип лямбды правильным типом, static_pointer_cast указав возвращаемое значение shared_ptr<Base> .

 auto f = std::async( [](){return std::static_pointer_cast<std::shared_ptr<Base>>std::make_shared<Derived>();});
 

Ответ №3:

Я бы настроил все так и чаще использовал ключевое слово auto. Фабрика животных выполняет все (неявное) приведение к базе (интерфейсу) для вас. Так что лямбда остается чище.

 #include <type_traits>
#include <iostream>
#include <future>

//-----------------------------------------------------------------------------
// declare an interface/abstract baseclass for animals.

struct animal_itf
{
    virtual ~animal_itf() = defau<
    virtual void make_noise() = 0;

protected:
    animal_itf() = defau<
};

//-----------------------------------------------------------------------------

struct bear_t final :
    public animal_itf
{
    void make_noise() override
    {
        std::cout << "I am a bear : GROWL" << std::endl;
    }
};

struct cat_t final :
    public animal_itf
{
    void make_noise() override
    {
        std::cout << "I am a cat : Meow" << std::endl;
    }
};

//-----------------------------------------------------------------------------
// animal factory

template<typename animal_t> 
std::shared_ptr<animal_itf> make_animal()
{
    return std::make_shared<animal_t>();
}

//-----------------------------------------------------------------------------

int main()
{
    auto future = std::async(std::launch::async, [](){ return make_animal<cat_t>(); });

    // show that the auto type IS a future holding a std::shared_ptr<animal_itf>
    static_assert(std::is_same_v<std::future<std::shared_ptr<animal_itf>>, decltype(future)>);

    auto animal_itf = future.get();
    animal_itf->make_noise();
}