Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ensure abstract bass class is a shared_ptr

I have an abstract base class:

struct Base : std::enable_shared_from_this<Base> 
{
    virtual ~Base() = default;
    virtual void foo() = 0;

    void bar() {
        baz(shared_from_this());
    }
};

The only valid use case for Base is to live in a shared_ptr - bar is an important method. How can I ensure that the following is impossible:

struct BadDerived : Base {
    void foo() override { ... }
};

BadDerived bd;
bd.bar(); 
like image 239
Barry Avatar asked Apr 16 '15 19:04

Barry


1 Answers

One technique is to make the constructor of Base private and friend a factory class or method:

struct Base : std::enable_shared_from_this<Base> 
{
    virtual ~Base() = default;
    virtual void foo() = 0;

    void bar() {
        baz(shared_from_this());
    }

private:
    template<class Impl> friend std::shared_ptr<Base> makeDerived();
    Base() {}
};

template<class Impl>
std::shared_ptr<Base> makeDerived() {
    struct Derived : Base, Impl {
        void foo() override { Impl::foo(static_cast<Base*>(this)); }
    };
    return std::make_shared<Derived>();
}

Usage:

struct Impl {
    void foo(Base* self) { std::cout << "Hello!" << std::endl; }
};
auto gd = makeDerived<Impl>();
gd->bar();

This does require you to rewrite any existing derived classes.

like image 197
ecatmur Avatar answered Oct 06 '22 01:10

ecatmur