C++11 智能指针 Make 可变大小对象函数

C++11 Smart Pointer Make Functions for Variable Sized Objects

假设我有一个 class 没有 public 构造函数,而是一个静态工厂或构建器方法,创建的对象的大小取决于传递给工厂(或构建器)的参数.

有没有办法使用 std::make_shared(或 std::make_unique)创建指向此类对象的共享(或唯一)指针?

对于任何建议的多态性,我更喜欢模板而不是虚拟方法。

Consider I have a class with no public constructor ...

Is there a way to create a shared (or unique) pointer to such an object using std::make_shared (or std::make_unique)?

没有这种方法。 std::make_sharedstd::make_unique 需要一个 public 构造函数。

相反,您可以创建共享(唯一)指针而无需方便的“make”函数:

// within the factory
return std::shared_ptr<T>(new T(size));

PS。 C++11 标准库没有 std::make_unique.

#include <memory>
#include <vector>
#include <iostream>

template <typename T>
std::unique_ptr<T> buildObjectWithSize(std::size_t size)  {
    return std::unique_ptr<T>{T::buildObject(size)};
}

class MyObject {
public:
    static MyObject* buildObject(std::size_t size) {
        return new MyObject(size);
    }

    int& operator[](int i) {
        return int_vector[i];
    }
private:
    MyObject(std::size_t size)
    : int_vector(size) {
    }

    std::vector<int> int_vector;
};

int main () {
    constexpr unsigned INT_VECTOR_SIZE{3U};

    std::unique_ptr<MyObject> my_object_up{buildObjectWithSize<MyObject>(INT_VECTOR_SIZE)};

    (*my_object_up.get())[1] = 5;
    std::cout << (*my_object_up.get())[1] << '\n';

    MyObject &my_object_ref = *my_object_up.get();
    my_object_ref[2] = 3;
    std::cout << my_object_ref[2] << '\n';
}

如果你有一个就地工厂方法可以在你提供的内存块中构造对象,再加上一个告诉你需要多少 space 的方法,你可以这样做.

我打算免费使用 C++14,如果你真的需要 C++11,请添加你自己的 typename::type

首先我们假设我们有这个:

struct some_args {
  // whatever
};
std::size_t how_big_is_X( some_args );
X* make_X( some_args, void* buffer );

有了上面的内容,我可以做一些功能上等同于 make_shared 的事情。

template<std::size_t Sz, std::size_t align=alignof(void*)>
struct smart_buffer_t {
  void(*dtor)(void*) = 0;
  std::aligned_storage_t< Sz, align> data;
  template<class T, class...Args>
  T* emplace(Args&&...args) {
    return ctor( [&](void* pdata) {
      ::new( pdata ) T(std::forward<Args>(args)...);
    } );
  }
  template<class F>
  T* ctor( F&& f ) {
    std::forward<F>(f)( (void*)&data );
    dtor = [](void* ptr){
      static_cast<T*>(ptr)->~T();
    };
    return static_cast<T*>((void*)&data);
  }
  ~smart_buffer_t() {
    if (dtor) dtor(&data);
  }
};

template<class T, std::size_t Sz, std::size_t Algn=alignof(void*), class F>
std::shared_ptr<T>
make_sized( F&& f ) {
  auto pbuff = std::make_shared<smart_buffer_t<Sz, Algn>>();
  T* r = pbuff->ctor( std::forward<F>(f) );
  return {r, pbuff}; // aliasing ctor
}

现在我们有:

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
using pow_t = index_t< (1<<I) >;

std::shared_ptr<X>
make_shared_X( some_args args ) {
  std::size_t Sz = how_big_is_X(args);
  using pmaker = std::shared_ptr<X>(*)(some_args);
  using maker_maker = [](auto Sz){
    return +[](some_args args) {
      return make_sized<X, Sz>([&](void* ptr){
        return make_X(args, ptr);
      });
    };
  };
  static const pmaker table[] = {
    maker_maker(pow_t<0>{}),
    maker_maker(pow_t<1>{}),
    maker_maker(pow_t<2>{}),
    // ...
    maker_maker(pow_t<63>{}), // assuming 64 bit size_t.
  };
  std::size_t i = 0;
  while(Sz > (1<<i))
    ++i;
  return table[i](args);
}

或类似的东西。

代码未经测试。它分配 2 的幂等于或大于您的 args 需求。但是对象是在与引用计数块相同的分配中构造的。

可以使用任何系列而不是 2 的幂,但是 table 大小必须足够大以处理来自 how_big_is_X.[=17= 的最大可能 return 值]