如何启用父级和派生级的_shared_from_this

发布于 2024-07-15 01:38:14 字数 1471 浏览 9 评论 0原文

我有简单的基类和派生类,我希望它们都具有 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());
    }
};

在以下代码中导致异常tr1::bad_weak_ptr

shared_ptr<foo> ptr(shared_ptr<foo>(new bar1));
function<void()> f=ptr->get_callback();
f();

所以在“谷歌搜索”之后我找到了以下解决方案:

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

现在它可以工作了。

有没有更好、更方便、更正确的方法来为父子双方enable_shared_from_this

谢谢

I have simple base and derived class that I want both have shared_from_this().

This simple solution:

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

Causes exception tr1::bad_weak_ptr in following code:

shared_ptr<foo> ptr(shared_ptr<foo>(new bar1));
function<void()> f=ptr->get_callback();
f();

So after "googling" I have found following solution:

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

And now it works.

Is there any better and more convinient and correct way to enable_shared_from_this for both parent and child?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

无声静候 2024-07-22 01:38:14

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

protected:
    template <typename Derived>
    std::shared_ptr<Derived> shared_from_base()
    {
        return std::static_pointer_cast<Derived>(shared_from_this());
    }

通过将其放置在基类中(以便重用)可以使这变得更加方便。

#include <memory>

template <class Base>
class enable_shared_from_base
  : public std::enable_shared_from_this<Base>
{
protected:
    template <class Derived>
    std::shared_ptr<Derived> shared_from_base()
    {
        return std::static_pointer_cast<Derived>(shared_from_this());
    }
};

然后从中推导出如下。

#include <functional>
#include <iostream>

class foo : public enable_shared_from_base<foo> {
    void foo_do_it()
    {
        std::cout << "foo::do_it\n";
    }
public:
    virtual std::function<void()> get_callback()
    {
        return std::bind(&foo::foo_do_it, shared_from_base<foo>());
    }
};

class bar1 : public foo {
    void bar1_do_it()
    {
        std::cout << "bar1::do_it\n";
    }
public:
    virtual std::function<void()> get_callback() override
    {
        return std::bind(&bar1::bar1_do_it, shared_from_base<bar1>());
    }
};

The OP solution can be made more convenient by defining the following on the base class.

protected:
    template <typename Derived>
    std::shared_ptr<Derived> shared_from_base()
    {
        return std::static_pointer_cast<Derived>(shared_from_this());
    }

This can be made more convenient by placing it in a base class (for reuse).

#include <memory>

template <class Base>
class enable_shared_from_base
  : public std::enable_shared_from_this<Base>
{
protected:
    template <class Derived>
    std::shared_ptr<Derived> shared_from_base()
    {
        return std::static_pointer_cast<Derived>(shared_from_this());
    }
};

and then deriving from it as follows.

#include <functional>
#include <iostream>

class foo : public enable_shared_from_base<foo> {
    void foo_do_it()
    {
        std::cout << "foo::do_it\n";
    }
public:
    virtual std::function<void()> get_callback()
    {
        return std::bind(&foo::foo_do_it, shared_from_base<foo>());
    }
};

class bar1 : public foo {
    void bar1_do_it()
    {
        std::cout << "bar1::do_it\n";
    }
public:
    virtual std::function<void()> get_callback() override
    {
        return std::bind(&bar1::bar1_do_it, shared_from_base<bar1>());
    }
};
2024-07-22 01:38:14

抱歉,没有。

问题是 shared_ptrshared_ptr 是不同的类型。 我不明白幕后发生的一切,但我认为当构造函数返回并分配给 shared_ptr 时,内部 weak_ptr 发现没有任何东西指向它(因为只有 shared_ptr 会增加计数器)并重置自身。 当您在 get_callback 中调用 bar1::shared_from_this 时,您会收到异常,因为内部 weak_ptr 未指向任何内容。

本质上,enable_shared_from_this 似乎只能从层次结构中的单个类透明地工作。 如果您尝试手动实现,问题应该变得明显。

Sorry, but there isn't.

The problem is that shared_ptr<foo> and shared_ptr<bar1> are different types. I don't understand everything that's going on under the hood, but I think that when the constructor returns and is assigned to a shared_ptr<foo>, the internal weak_ptr<bar1> sees that nothing is pointing to it (because only a shared_ptr<bar1> would increment the counter) and resets itself. When you call bar1::shared_from_this in get_callback, you get the exception because the internal weak_ptr isn't pointing to anything.

Essentially, enable_shared_from_this only seems to work transparently from a single class in a hierarchy. If you try implementing it manually, the problem should become obvious.

江湖正好 2024-07-22 01:38:14

如果您想要实现 shared_from_this() 函数,则与 @evoskuil 类似的解决方案可以减少派生类中的样板文件,从而在类中使用时生成以下代码:

auto shared_from_this() {
    return shared_from(this);
}  

This using 'shim'functions课堂之外。 通过这种方式,它还为接口无法修改但派生自 enable_shared_from_this 的类提供了一种干净的方法 - 例如

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

上面的代码依赖于这样一个事实:传递给 shared_from(...) 的类型继承自 std::enable_shared_from_this在其祖先的某个时刻。

调用shared_from_base将找出最终的类型。 由于我们知道 That 继承自 Base,因此可以进行静态向下转型。

可能存在一些病态的极端情况,其中的类具有类型转换运算符..但这不太可能发生在未设计来破坏此问题的代码中。

示例:

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

https://onlinegdb.com/SJWM5CYIG< /a>

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

旧的实现:

#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;
}

A similar solution to @evoskuil that reduces boilerplate in derived classes should you want to implement a shared_from_this() function, resulting in the following code at the point of use in the class:

auto shared_from_this() {
    return shared_from(this);
}  

This uses 'shim' functions outside of the class. By doing it that way it also provides a clean way to do this for classes who's interface can't be modified but derive from enable_shared_from_this - e.g.

auto shared_that = shared_from(that);

Note: Use of auto for return types here will depend upon the age of your compiler.

Shim functions that could be placed in a library header:

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

The above code relies on the fact that the type passed to shared_from(...) inherits from std::enable_shared_from_this<Base> at some point in its ancestry.

Calling shared_from_base will figure out what type that ultimately was. Since we know that That inherits from Base, a static downcast can be made.

Probably there are some pathological corner cases with classes having type conversion operators.. but that's unlikely to occur in code not designed to break this.

Example:

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

Compilation test:

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

https://onlinegdb.com/SJWM5CYIG

Prior solution that I posted, kept to make the comments still make sense - this placed the functions in the base class which had some problems - particularly non-uniformity between the required implementation for 'normal' classes and template classes.
Additionally the implementation in the base class would need to be repeated for new class hierarchies which is not all that DRY.
Furthermore the base class function suffered from the possibility of misuse by supplying a base class pointer from a different object. The newer scheme above avoids this entirely and the runtime assert(...) check goes.

Old implementation:

#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;
}
遮了一弯 2024-07-22 01:38:14

很容易; 仅在您的基类中继承 public shared_from_this 。 在派生类中实现一个访问器,将其转换为适当的类型;

std::shared_ptr<Derived> shared() 
{
   return std::dynamic_pointer_cast<Derived>(Base::shared_from_this());
}

Quite easy; inherit public shared_from_this only in your base class. Implement an accessor in your derived class that casts to the appropriate type;

std::shared_ptr<Derived> shared() 
{
   return std::dynamic_pointer_cast<Derived>(Base::shared_from_this());
}
遗弃M 2024-07-22 01:38:14
template<class T>
class Base : public std::enable_shared_from_this<T> {
};

template<class T>
class Intermediate : public Base<T> {
};

class Derived : public Intermediate<Derived> {
  public:

  std::shared_ptr<Derived> getDerived() { return shared_from_this(); }
};
template<class T>
class Base : public std::enable_shared_from_this<T> {
};

template<class T>
class Intermediate : public Base<T> {
};

class Derived : public Intermediate<Derived> {
  public:

  std::shared_ptr<Derived> getDerived() { return shared_from_this(); }
};
莫言歌 2024-07-22 01:38:14

通过 c++23 推导这一点,事情变得容易多了。 https://godbolt.org/z/j499WK58Y

#include <memory>
#include <iostream>
#include <functional>
using namespace std;

struct new_enable_shared_from_this :
    public std::enable_shared_from_this<new_enable_shared_from_this> {
    
    template <typename Self>
    auto new_shared_from_this(this Self& self) {
        return std::static_pointer_cast<Self>(self.shared_from_this());
    }
};

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

class bar1 : public foo {
    void bar1_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    virtual function<void()> get_callback()
    {
        return bind(&bar1::bar1_do_it,new_shared_from_this());
    }
};

int main() {
    auto pf = std::make_shared<foo>();
    pf->get_callback()();
    auto pb = std::make_shared<bar1>();
    pb->get_callback()();
}

这是之前的答案:

嗯,我不知道不喜欢虚函数。 虚函数只是用于类型擦除,但我们并不总是需要类型擦除。 因此,提供一种类型擦除机制就足够了。 这是一个不使用虚函数的示例:

#include <iostream>
#include <functional>
#include <memory>
using namespace std;

template<typename derived>
class foo_imp {
    void foo_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    function<void()> get_callback()
    {
        auto&& d = static_cast<derived&>(*this);
        return bind(&foo_imp::foo_do_it, d.shared_from_this());
    }
};

template<typename derived>
class bar_imp {
    void bar_do_it()
    {
        cout<<"bar::do_it\n";
    }
public:
    function<void()> get_callback()
    {
        auto&& d = static_cast<derived&>(*this);
        return bind(&bar_imp::bar_do_it, d.shared_from_this());
    }
};

struct foo : public foo_imp<foo>, public enable_shared_from_this<foo> {};
struct bar : public bar_imp<bar>, public enable_shared_from_this<bar> {};

struct v_foo {
    virtual function<void()> get_callback() = 0;
};

template <typename T>
std::shared_ptr<v_foo> convert(const std::shared_ptr<T>& st) {
    struct _ : public v_foo {
        _(const std::shared_ptr<T>& st) : _st{st} {}
        function<void()> get_callback() override {
            return _st->get_callback();
        }
        std::shared_ptr<T> _st;
    };
    return std::make_shared<_>(st);
}

int main() {
    auto sf = make_shared<bar>();
    sf->get_callback()();

    auto svf = convert(sf);
    svf->get_callback()();

    auto sb = make_shared<foo>();
    sb->get_callback()();

    auto svb = convert(sb);
    svb->get_callback()();
}

With c++23 deducing this, things become much easier. https://godbolt.org/z/j499WK58Y

#include <memory>
#include <iostream>
#include <functional>
using namespace std;

struct new_enable_shared_from_this :
    public std::enable_shared_from_this<new_enable_shared_from_this> {
    
    template <typename Self>
    auto new_shared_from_this(this Self& self) {
        return std::static_pointer_cast<Self>(self.shared_from_this());
    }
};

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

class bar1 : public foo {
    void bar1_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    virtual function<void()> get_callback()
    {
        return bind(&bar1::bar1_do_it,new_shared_from_this());
    }
};

int main() {
    auto pf = std::make_shared<foo>();
    pf->get_callback()();
    auto pb = std::make_shared<bar1>();
    pb->get_callback()();
}

This is the previous answer:

Well, I don't like virtual function. Virtual function is just for type erasing, but we don't need type erasing all the time. So, provide a mechanism for type erasing is enough. Here is an example that don't use virtual function:

#include <iostream>
#include <functional>
#include <memory>
using namespace std;

template<typename derived>
class foo_imp {
    void foo_do_it()
    {
        cout<<"foo::do_it\n";
    }
public:
    function<void()> get_callback()
    {
        auto&& d = static_cast<derived&>(*this);
        return bind(&foo_imp::foo_do_it, d.shared_from_this());
    }
};

template<typename derived>
class bar_imp {
    void bar_do_it()
    {
        cout<<"bar::do_it\n";
    }
public:
    function<void()> get_callback()
    {
        auto&& d = static_cast<derived&>(*this);
        return bind(&bar_imp::bar_do_it, d.shared_from_this());
    }
};

struct foo : public foo_imp<foo>, public enable_shared_from_this<foo> {};
struct bar : public bar_imp<bar>, public enable_shared_from_this<bar> {};

struct v_foo {
    virtual function<void()> get_callback() = 0;
};

template <typename T>
std::shared_ptr<v_foo> convert(const std::shared_ptr<T>& st) {
    struct _ : public v_foo {
        _(const std::shared_ptr<T>& st) : _st{st} {}
        function<void()> get_callback() override {
            return _st->get_callback();
        }
        std::shared_ptr<T> _st;
    };
    return std::make_shared<_>(st);
}

int main() {
    auto sf = make_shared<bar>();
    sf->get_callback()();

    auto svf = convert(sf);
    svf->get_callback()();

    auto sb = make_shared<foo>();
    sb->get_callback()();

    auto svb = convert(sb);
    svb->get_callback()();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文