【问题标题】:How to push_back a "unique_ptr<Base>&" to "vector<unique_ptr<Derived>>"如何将“unique_ptr<Base>&”推回“vector<unique_ptr<Derived>>”
【发布时间】:2021-10-09 13:44:08
【问题描述】:

我需要通过一些条件检查在 2 vector&lt;unique_ptr&gt; 之间移动元素。移动后,我将忽略from-vector(将所有权转移到to-vector)。

案例1:从vector&lt;unique_ptr&lt;Derived&gt;&gt; fromDeriveds移动到vector&lt;unique_ptr&lt;Base&gt;&gt; toBases

vector<unique_ptr<Derived>> fromDeriveds;
vector<unique_ptr<Base>> toBases;
   
for (unique_ptr<Derived> &derived: fromDeriveds)
{
    if (derived->prop == 1)
    {
        toBases.push_back(move(derived));
    }
}

这个案子不错。

案例2:从vector&lt;unique_ptr&lt;Base&gt;&gt; fromBases移动到vector&lt;unique_ptr&lt;Derived&gt;&gt; toDeriveds

vector<unique_ptr<Base>> fromBases;
vector<unique_ptr<Derived>> toDeriveds;
   
for (unique_ptr<Base> &base: fromBases)
{
    Derived *derived = dynamic_cast<Derived *>(base.get());
    if (derived && derived->prop == 1)
    {
        toDeriveds.push_back(move(base));
    }
}

编译失败,报如下错误:

main.cpp:44:44: error: no matching function for call to ‘std::vector<std::unique_ptr<Derived> >::push_back(std::remove_reference<std::unique_ptr<Base>&>::type)’
             toDeriveds.push_back(move(base));
                                            ^

虽然我可以通过以下方式做到,但它是复制而不是移动:

vector<unique_ptr<Base>> fromBases;
vector<unique_ptr<Derived>> toDeriveds;
   
for (unique_ptr<Base> &base: fromBases)
{
    Derived *derived = dynamic_cast<Derived *>(base.get());
    if (derived && derived->prop == 1)
    {
        toDeriveds.push_back(make_unique<Derived>(*derived));
    }
}

有没有更好的方法来实现目标?

【问题讨论】:

标签: c++ vector polymorphism unique-ptr


【解决方案1】:

您可以在 fromBase 列表上进行迭代并进行检查,如果满足该要求,那么您可以简单地 release 指针 - 这会在不调用析构函数的情况下放弃基指针。只要确保删除这些释放的空指针,以防您需要再次使用fromBase向量。

WANDBOX LINK

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

using namespace std;

class Base{ public: virtual ~Base() = default; };
class Derived: public Base {};


int main() {
    vector<unique_ptr<Base>> fromBase;
    vector<unique_ptr<Derived>> toDerived;
    
    fromBase.emplace_back(make_unique<Base>());
    fromBase.emplace_back(make_unique<Base>());
    fromBase.emplace_back(make_unique<Derived>());
    
    for(auto& p: fromBase) {
        Derived* d = dynamic_cast<Derived *>(p.get());
        // make other checks here
        if(d != nullptr) {
            toDerived.emplace_back(d);
            p.release();
        }
    }

    // make sure to remove released pointers
    fromBase.erase(remove_if(fromBase.begin(), fromBase.end(), [](const auto& p) { return !p; }), fromBase.end());
    
    cout << fromBase.size() << " " << toDerived.size() << endl;
}

【讨论】:

  • 稍微简洁一点的是在擦除调用中使用remove(fromBase.begin(), fromBase.end(), nullptr)
  • 在C++20中,可以使用std::erase(fromBase, nullptr);
  • 感谢大家的建议。因为在获取记录并放置到 toDerived 之后我不关心 fromBase (所以可以将 fromBase 排除在范围之外)。我将其更改为“Derived* d = dynamic_cast(p.release());”,这也会在拥有新的 unique_ptr (wandbox.org/permlink/s0yfBPweNQKv3Fz5) 之前释放所有权。但有一点我不完全是你在“p.release();”之前调用“toDerived.emplace_back(d)”。由于同一个对象有 2 个 unique_ptr,这会导致意外行为吗? (wandbox.org/permlink/BaHW7dBhScUIkqYM)
  • @kzfid 不,这不应该是未定义的行为,但是,我仍然建议保持代码更清洁,因为它没有任何危害。另外,如果您认为我的回答有帮助,您也可以标记它:)
  • 绝对有帮助。 ^_^
猜你喜欢
  • 2018-12-15
  • 2020-09-29
  • 2014-03-27
  • 2019-05-24
  • 2014-07-21
  • 2018-03-12
  • 2020-01-30
  • 1970-01-01
  • 2016-07-23
相关资源
最近更新 更多