如何在不实例化的情况下将堆栈对象移动到 std::shared_pointer

How to move stack object into std::shared_pointer without instantiation

问题

你好。我有一个有点奇怪的问题。假设您有一个不可复制的堆栈对象,您是从外部库中获得的。如何在不实际实例化 class 的情况下将内容移动到 std::make_shared 分配的堆对象中?

class my_class {
private:
  my_class(my_class const&) = delete;

public:
  my_class(some_arg_t some_args);
  my_class(my_class&& move);
}

// later on

std::unordered_set<std::shared_ptr<my_class>> shared_pointers_container;

int foo() {
  my_class obj = getMyClassObject();
  shared_pointers_container.insert(// moving obj into heap memory and get the shared pointer to it)
}

一个“解决方案”可能是创建对象的实例然后替换它,如下所示

  std::shared_ptr<my_class> ptr = std::make_shared<my_class>(arguments_needed);
  *ptr.get() = std::move(obj);
  shared_pointers_container.insert(ptr);

但这不是一个好的解决方案(以防构造函数进行一些更改)。

也许有一种方法可以告诉 std::make_shared 从指定对象中移动新创建对象的内容?

可以做到。不需要技巧。只需移动对象:

shared_pointers_container.insert(std::make_shared<my_class>(getMyClassObject()));

// or if you need to start from `obj`:

my_class obj = getMyClassObject();
// work with obj
shared_pointers_container.insert(std::make_shared<my_class>(std::move(obj)););

关于您的评论:

without actually instantiating the class?

(in case the constructor does some changes).

这完全没有道理。如果构造函数没有做它应该做的事情,那么 class 就是错误的。

更清楚地说:您要解决的问题是:您有一个从外部库中按值给出的对象。该对象是只能移动的。你想把这个对象放到一个共享指针的容器中。这就是问题。在解决这个问题时,您认为您不能使用移动构造函数,但事实并非如此。解决方案是使用我展示的移动构造函数。

以防万一您出于某种原因需要它,这里有一个适用于既不可移动也不可复制的 class 的技巧。这有点可怕;我不会在实践中推荐它。

class Wrapper {
    std::aligned_storage_t<sizeof(my_class), alignof(my_class)> data;
public:
    Wrapper() { new(&data) my_class{getMyClassObject()}; }
    ~Wrapper() { get()->~my_class(); }
    my_class* get() { return reinterpret_cast<my_class*>(&data); }
};

int main()
{
    auto wp = std::make_shared<Wrapper>();
    std::shared_ptr<my_class> p(wp, wp->get());
}

Demo.

这依赖于std::shared_ptr鲜为人知的特性,它可以管理一个对象,但暴露一个指向另一个对象(通常是第一个对象的子对象)的指针。