是否可以避免复制传递给保存指向它的指针的函数的临时 class 实例?

Is it possible to avoid copying a temporary class instance passed to a function that saves a pointer to it?

我有一个 class Holder,其中包含一个向量,该向量包含从相同的抽象基础 class 派生的不同 classes(例如 Derived) ](Base)。我将它构造为指向基 class 的指针的向量以支持多态性。通过名为 add 的方法将新实例添加到向量中。新的 classes 是在调用 add 时在括号内构造的,并且没有命名(此机制已提供给我,无法更改)。

我不太明白这个构造的class的生命周期是多少,如何防止它在退出add后立即被销毁。它必须被构建,那么有没有办法让它保持活动状态并持有指向它的有效指针?还是我必须创建一个副本并让通过的临时文件消失?

我正在使用 C++17,我的 classes 中的 none 手动分配内存,仅使用 STL 容器和原始数据类型。我一直在查看 std::move() 但我没有找到我需要的东西。示例代码:

class Base
{
public:
  std::vector<int> vec;
  virtual void foo() = 0;
}
class Derived : public Base
{
public:
  Derived(int a, int b)
  {
    vec.push_back(a);
    vec.push_back(b);
  }
  void foo() override;
}
class Holder
{
public:
  std::vector<Base*> ptrs;
  void add(Base& arg)     // What should be the type of arg? (Does not have to be Base&)
  {
    ptrs.push_back(&arg); // How should I add a pointer to arg into ptrs so that
                          // arg stays alive after returning from this method?
  }
}
int main()
{ 
  Holder h;
  h.add(Derived(1, 2));   // this is how add() gets called, I cannot change it
  ...
}

编辑:为了澄清,我可以更改 add 的签名和正文以及向量 ptrs 使用的数据类型。我无法更改 add 的调用方式 (h.add(Derived(1, 2));).

main

传递的参数是在调用 add returns 之后临时销毁的。您可以存储此值、地址,但绝不能取消引用它。

如果您想确保对象在 Holder 的生命周期内保持有效,您唯一的选择是接管现有对象的所有权或制作您拥有所有权的副本。

获取现有对象的所有权

为此,最好使用 std::unique_ptr 来明确所有权。

class Holder
{
    ...
    void add(std::unique_ptr<Base>&& arg)
    {
        //preferrably make ptrs a std::vector<std::unique_ptr<Base>> and use ptrs.emplace_back(std::move(arg)) or
        ptrs.push_back(args.get());
        args.release(); // make sure the object gets deleted before or during destruction of the Holder object
    }
};

复制

在此处使用移动构造函数来作废副本

class Holder
{
    ...

    template<class T>
    void add(T&& arg)     // What should be the type of arg?
    {
        ptrs.push_back(new T(std::forward<T>(arg));
        // Note: make sure the object created here gets destroyed
    }
};

编辑

根据更新的答案,这是 Holder 的实现,我会选择:

class Holder
{
public:
    std::vector<std::unique_ptr<Base>> ptrs; // make this private?

    template<class T>
    void add(T&& arg)
    {
        // better compiler error on incorrect use
        static_assert(std::is_base_of_v<Base, T>,
                      "parameter passed must be an object of a class derived from Base");

        ptrs.reserve(ptrs.size() + 1); // if an std::bad_alloc happens, this gives you a strong exception guarantee
        
        ptrs.emplace_back(std::make_unique<T>(std::forward<T>(args)));
    }
};

注意:这仅适用于 Base 的虚拟析构函数;如果这不是一个选项,您将需要保存调用对象旁边的正确析构函数的逻辑。您不会阻止临时 Derived 对象的创建,但至少可以使用 Derived 的隐式创建的移动构造函数来防止不必要地复制 Base::vec[=22 的元素=]