在没有模板的情况下使用任意所属指针

Using arbitrary owning pointer without templating

本文关键字:任意所 指针 情况下      更新时间:2023-10-16

我想将对象的(共享)所有权传递给函数foo::bar。问题是,我不在乎所有权是独占的还是共有的。我认为class foo是一个接口,我不想关心所有权细节。我所关心的是,我可以确保传递的智能指针的生命周期超过foo::bar的返回。

现在我可以写

class foo {
  void bar( std::shared_ptr<blub> );
  void bar( std::unique_ptr<blub> );
};

,但这是不优雅的时候,我们积累了几个智能指针的变体。为每个变体编写重载是相当麻烦的,特别是如果我想在代码中多次使用这种方案。

现在我也不希望foo被模板化,因为明显的模板复杂性。

我能想到的最简单的方法就是一个智能指针包装器

template <typename T>
class owning_ptr {
  // Sorts out the largest type so we can allocate enough space
  typedef largest<std::shared_ptr<T>, std::unique_ptr<T>, myptr<T>>::type big_type;
  typedef std::aligned_storage <sizeof(big_type), std::alignment_of<big_type>::value>::type buffer_type;
  buffer_type _internal;
  int _type;
};

有点低效。是否有更好的方法来构造包装器?

最后我真的很想要签名:

class foo {
  void bar( owning_ptr<blub> );
};

注意:底部有一个有趣的编辑

如果你不想对所属指针类型进行参数化或重载,你可能最好采用shared_ptr<>并强制转换unique_ptr<>。我能想到的任何非模板解决方案都至少具有与shared_ptr相同的开销,大多数需要多态调用,自由存储分配,或两者兼而有。

您可以通过采用某种类型的代理转换器来自动从shared_ptr进行转换,这应该允许您在没有强制转换或显式转换(move()除外)的情况下获取unique_ptr r值或shared_ptr,并提取可用的shared_ptr以存储在您的实例中。下面是一个例子:

#include <iostream>
#include <memory>
#include <utility>
using namespace std;
// Take an instance of gimme_owned_ptr<T> to save off a shared_ptr<T>
// from either a shared_ptr or unique_ptr.
template<typename T>
struct gimme_owned_ptr {
    // Make the two constructors templates so we can accept
    // any convertible owning pointer:
    template<typename S>
    gimme_owned_ptr( unique_ptr<S> p )
        : p_( shared_ptr<S>( move(p) ) )
    { }
    template<typename S>
    gimme_owned_ptr( shared_ptr<S> p )
        : p_( p )
    { }
    shared_ptr<T> get() const {
        return p_;
    }
private:
    shared_ptr<T> p_;
};
struct Foo {
    void operator()() const { v_call(); }
    virtual ~Foo() { }
private:
    virtual void v_call() const = 0;
};
struct Bar {
    Bar( gimme_owned_ptr<Foo const> const &gop )
        : foo_( gop.get() )
    { }
    void operator()() const { (*foo_)(); }
private:
    shared_ptr<Foo const> foo_;
};
struct Baz : Foo {
private:
    virtual void v_call() const { cout << "Baz()()n"; }
};
int main() {
    unique_ptr<Baz> upf( new Baz() );
    shared_ptr<Baz> spf( new Baz() );
    Bar upb( move( upf ) );
    Bar spb( spf );
    upb();
    spb();
}

是的,此解决方案确实使用模板,但仅用于gimme_owned_ptr实用程序类模板,并且仅用于重用和转换。您自己的类(在本例中是Bar)不需要是模板,也不需要重新实现gimme_owned_ptr来与其他类型一起使用它。

======== EDIT ========

我刚刚检查过,从unique_ptrshared_ptr的内置转换基本上完成了我上面写的gimme_owned_ptr类的所有操作。如果类的类型为shared_ptr<T>,则传递move(unique_ptr<S>)将导致可操作的转换。因此,您真正需要做的就是使用shared_ptr,用户唯一需要做的就是在unique_ptr s上调用move()(无论如何他们都应该这样做)。使用gimme_owned_ptr的唯一原因是接受不同的指针组合或以不同的方式接受它们。(例如,您可以通过获取unique_ptr<S>&并在内部调用move()来使move()变得不必要,但这可能是一个坏主意,因为它将默默地夺取所有权。)