C++ 如何从父级和派生级的\u启用\u共享\u

C++ 如何从父级和派生级的\u启用\u共享\u,c++,boost,smart-pointers,C++,Boost,Smart Pointers,我有一个简单的基类和派生类,我希望这两个类都有shared\u from\u this() 这个简单的解决方案: class foo : public enable_shared_from_this<foo> { void foo_do_it() { cout<<"foo::do_it\n"; } public: virtual function<void()> get_callback() {

我有一个简单的基类和派生类,我希望这两个类都有
shared\u from\u this()

这个简单的解决方案:

class foo : public enable_shared_from_this<foo> {
    void foo_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    virtual function<void()> get_callback()
    {
        return boost::bind(&foo::foo_do_it,shared_from_this());
    }
    virtual ~foo() {};
};

class bar1 : public foo , public enable_shared_from_this<bar1> {
    using enable_shared_from_this<bar1>::shared_from_this;
    void bar1_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    virtual function<void()> get_callback()
    {
        return boost::bind(&bar1::bar1_do_it,shared_from_this());
    }
};
class bar2 : public foo {
    void bar2_do_it()
    {
        cout<<"foo::do_it\n";
    }
    shared_ptr<bar2> shared_from_this()
    {
        return boost::static_pointer_cast<bar2>(foo::shared_from_this());
    }
public:
    virtual function<void()> get_callback()
    {
        return boost::bind(&bar2::bar2_do_it,shared_from_this());
    }
};
所以在“谷歌搜索”之后,我找到了以下解决方案:

class foo : public enable_shared_from_this<foo> {
    void foo_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    virtual function<void()> get_callback()
    {
        return boost::bind(&foo::foo_do_it,shared_from_this());
    }
    virtual ~foo() {};
};

class bar1 : public foo , public enable_shared_from_this<bar1> {
    using enable_shared_from_this<bar1>::shared_from_this;
    void bar1_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    virtual function<void()> get_callback()
    {
        return boost::bind(&bar1::bar1_do_it,shared_from_this());
    }
};
class bar2 : public foo {
    void bar2_do_it()
    {
        cout<<"foo::do_it\n";
    }
    shared_ptr<bar2> shared_from_this()
    {
        return boost::static_pointer_cast<bar2>(foo::shared_from_this());
    }
public:
    virtual function<void()> get_callback()
    {
        return boost::bind(&bar2::bar2_do_it,shared_from_this());
    }
};
class bar2:公共foo{
void bar2_do_it()
{

对不起,没有

问题是
shared_ptr
shared_ptr
是不同的类型。我不理解引擎盖下发生的一切,但我认为当构造函数返回并被分配到
shared_ptr
时,内部
弱_ptr
会看到没有任何东西指向它(因为只有一个
shared\u ptr
会增加计数器)并重置自身。当您在
get\u回调中从该
调用
bar1::shared\u时,会出现异常,因为内部
弱\u ptr
没有指向任何东西


本质上,
enable\u shared\u from\u此
似乎只能从层次结构中的单个类透明地工作。如果您尝试,问题应该会变得很明显。

通过在基类上定义以下内容,OP解决方案可以变得更加方便

protected:
    template <typename Derived>
    std::shared_ptr<Derived> shared_from_base()
    {
        return std::static_pointer_cast<Derived>(shared_from_this());
    }
受保护:
模板
std::shared_ptr shared_from_base()
{
返回std::static_pointer_cast(来自_this()的共享_);
}

一个类似于@evoskuil的解决方案,它可以减少派生类中的样板文件,如果您想实现一个
shared\u from\u this()
函数,则会在类中的使用点生成以下代码:

auto shared_from_this() {
    return shared_from(this);
}  
这在类之外使用了“shim”函数。通过这种方式,它还为不能修改接口但从
派生的类提供了一种干净的方法,可以从该类启用共享\u

auto shared_that = shared_from(that);
注意:此处对返回类型使用
auto
取决于编译器的使用年限

可以放置在库标题中的填充函数:

template <typename Base>
inline std::shared_ptr<Base>
shared_from_base(std::enable_shared_from_this<Base>* base) 
{
    return base->shared_from_this();
}
template <typename Base>
inline std::shared_ptr<const Base>
shared_from_base(std::enable_shared_from_this<Base> const* base) 
{
    return base->shared_from_this();
}
template <typename That>
inline std::shared_ptr<That>
shared_from(That* that) 
{
    return std::static_pointer_cast<That>(shared_from_base(that));
}
编译测试:

struct base : public std::enable_shared_from_this<base> {};
struct derived : public base
{
    auto shared_from_this() {
        return shared_from(this);
    }
    // Can also provide a version for const:
    auto shared_from_this() const {
        return shared_from(this);
    }
    // Note that it is also possible to use shared_from(...) from
    // outside the class, e.g. 
    // auto sp = shared_from(that);
};
template <typename X>
struct derived_x : public derived
{
    auto shared_from_this() {
        return shared_from(this);
    }
};
int main()
{
    auto pbase = std::make_shared<base>();
    auto pderived = std::make_shared<derived>();
    auto pderived_x = std::make_shared<derived_x<int> >();

    auto const& const_pderived = *pderived;
    const_pderived.shared_from_this();

    std::shared_ptr<base> test1 = pbase->shared_from_this();
    std::shared_ptr<derived> test2 = pderived->shared_from_this();
    std::shared_ptr<derived_x<int> > test3 = pderived_x->shared_from_this();

    return 0;
}
intmain()
{
auto pbase=std::使_共享();
auto-pderived=std::make_shared();
auto-pderived_x=std::make_shared();
自动常数&const_pderived=*pderived;
const_pderived.shared_from_this();
std::shared_ptr test1=pbase->shared_from_this();
std::shared_ptr test2=pderived->shared_from_this();
std::shared_ptr test3=pderived_x->shared_from_this();
返回0;
}

我发布的先前解决方案旨在使评论仍然有意义-这将函数放置在基类中,存在一些问题-特别是“普通”类和模板类所需实现之间的不一致性。
此外,对于新的类层次结构,基类中的实现需要重复进行,而新的类层次结构并不是那么枯燥。 此外,基类函数还可能因从不同的对象提供基类指针而被误用。上面较新的方案完全避免了这一点,运行时断言(…)检查也会进行

旧的实施:

#include <cassert>
#include <memory>

class base : public std::enable_shared_from_this<base>
{
protected:   
    template <typename T>
    std::shared_ptr<T> shared_from(T* derived) {
        assert(this == derived);
        return std::static_pointer_cast<T>(shared_from_this());
    }
};

class derived : public base
{
public:
    auto shared_from_this() {
        return shared_from(this);
    }
};

template <typename X>
class derived_x : public derived
{
public:
    auto shared_from_this() {
        return this->template shared_from(this);
    }
};

int main()
{
    auto pbase = std::make_shared<base>();
    auto pderived = std::make_shared<derived>();
    auto pderived_x = std::make_shared<derived_x<int> >();

    std::shared_ptr<base> test1 = pbase->shared_from_this();
    std::shared_ptr<derived> test2 = pderived->shared_from_this();
    std::shared_ptr<derived_x<int> > test3 = pderived_x->shared_from_this();

    return 0;
}
#包括
#包括
类基:public std::从\u中启用\u共享\u
{
受保护的:
模板
std::shared_ptr shared_from(T*派生){
断言(this==派生);
返回std::static_pointer_cast(来自_this()的共享_);
}
};
派生类:公共基
{
公众:
从_this()自动共享_{
从(此)返回共享的_;
}
};
模板
类派生的\u x:公共派生的
{
公众:
从_this()自动共享_{
从(此)返回此->模板共享\u;
}
};
int main()
{
auto pbase=std::使_共享();
auto-pderived=std::make_shared();
auto-pderived_x=std::make_shared();
std::shared_ptr test1=pbase->shared_from_this();
std::shared_ptr test2=pderived->shared_from_this();
std::shared_ptr test3=pderived_x->shared_from_this();
返回0;
}

我尝试手动实现它,实际上并不难。在
My::SmartPointer
中,检查T是否派生自
My::enableSmartFromThis
。如果是,不要在堆上分配引用计数器,而是使用
My::enableSmartFromThis
的成员。现在
My::GetSmartFromThis(this)
变得微不足道,它可以将
转换为
My::enableSmartFromThis*
并找到现有的引用计数。您甚至可以检查您是否正在尝试从基转换为派生,而仅从
My::enableSmartFromThis
派生。这非常优雅。+1您为什么使用
模板
关键字in
derived_x
?@Myon这是因为shared_from是基类中的模板成员函数,它是从模板类中调用的。如果没有此处的“this”或“template”关键字,它将无法在兼容的编译器上编译。旧版本的MSVC可能编译得很好。没有它,编译器将解释“@Myon实际上想出了一个更好的方法,完全避免了丑陋的模板类不一致性,并且还有许多其他积极的副作用-参见更新的答案。