Home > Enterprise >  Cannot use std::future to store polymorphic object
Cannot use std::future to store polymorphic object

Time:09-21

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>();});
}

This gives the following error :

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

However, this compiles :

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

Could you please explain why? And what is the recommended pattern to create futures to hold polymorphics objects?

CodePudding user response:

You must explicitly convert the result of make_shared<Derived>() into a 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

CodePudding user response:

The return type of your lambda is a shared_ptr<Derived>. Therefore, the future that async will create contains a shared_ptr<Derived>. If you want it to have a different type, you need to make the lambda's return type the correct type, by static_pointer_casting the return value to shared_ptr<Base>.

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

CodePudding user response:

I would set things up like this and make more use of the auto keyword. The animal factory does al the (implicit) casting to base (interface) for you. So the lambda stays cleaner.

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

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

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

protected:
    animal_itf() = default;
};

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

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();
}
  • Related