【问题标题】:re-order vector<std::unique_ptr<X>> given a vector<const X*>给定向量<const X*> 重新排序向量<std::unique_ptr<X>>
【发布时间】:2013-09-17 10:57:29
【问题描述】:

我有一个vector&lt;std::unique_ptr&lt;X&gt;&gt; 和一个vector&lt;const X*&gt;,它们包含相同的指针集,但顺序不同。我希望唯一指针向量的排序与 const 指针向量完全相同。

我可以这样做的一种方法是:

vector<std::unique_ptr<X>> uniq_ptrs;
vector<const X*> const_ptrs;
vector<std::unique_ptr<X>> tmp(uniq_ptrs.size());

hash_map<const X*, int> indices;
for (int i = 0; i < const_ptrs->size(); ++i) {
  indices[const_ptrs[i]] = i;
}
for (std::unique_ptr<X>& uniq_ptr : uniq_ptrs) {
  tmp[indices[uniq_ptr.get()]].swap(uniq_ptr);
}
uniq_ptrs.swap(tmp)

就地版本,仍然带有 hash_map:

vector<const X*> const_ptrs;

hash_map<const X*, int> indices;
for (int i = 0; i < const_ptrs.size(); ++i) {
  indices[const_ptrs[i]] = i;
}
for (int i = 0; i < const_ptrs.size(); ++i) {
  std::swap(uniq_ptrs[i], uniq_ptrs[indices[const_ptrs[i]]]);
}

但我希望有一种更好的方法,它不涉及临时向量、哈希映射和两次遍历数据。

【问题讨论】:

  • 辅助结构为您提供了重新排列的线性复杂性。没有它,您需要更频繁地遍历范围。
  • 既然你只是在处理指针,你能不能不创建一个新的vector<:unique_ptr>> 循环遍历vector const_ptrs.
  • @jayadev,我将不得不通过强制转换来消除常量。
  • 您的就地版本看起来完全有缺陷,确定没有错别字吗?
  • @brunocodutra,是的 - 我修好了。

标签: c++ c++11 vector stl unique-ptr


【解决方案1】:

由于const_ptrs 包含与uniq_ptrs 相同的 指针,因此根本不需要使用后者,并且也不需要临时 .但是,您必须注意不要删除对象。

// release the unique_ptrs of their ownership
for(auto&x : uniq_ptrs)
    x.release();
// fill the array with the pointers in another order
uniq_ptrs.clear();
for(auto x : const_ptrs)
    uniq_ptrs.emplace_back(const_cast<X*>(x));   // const_cast okay here

【讨论】:

  • 值得注意的是,这依赖于 std::default_delete&lt;X&gt; 的所有实例都是等效的这一事实(该类型没有状态),因此丢失并重新创建删除器并不重要.
  • 如前所述,const_cast&lt;&gt; 在这种情况下是安全的——它是同一组指针,只是顺序不同。 +1 可读性和使用 emplace_back() :)
【解决方案2】:

如果性能没有问题:

auto idx_of = [&](X const* p) {
    return std::distance(begin(const_ptrs), std::find(begin(const_ptrs), end(const_ptrs), p)); 
};
std::sort(begin(uniq_ptrs), end(uniq_ptrs), [&](std::unique_ptr<X> const& a, std::unique_ptr<X> const& b) {
            return idx_of(a.get()) < idx_of(b.get());
        });

如果性能是一个问题,请使用中间图:

hash_map<const X*, int> indices;
for (auto i = 0u; i < const_ptrs.size(); ++i) {
    indices[const_ptrs[i]] = i;
}

std::sort(begin(uniq_ptrs), end(uniq_ptrs), [&](std::unique_ptr<X> const& a, std::unique_ptr<X> const& b) {
            return indices[ a.get() ] < indices[ b.get() ];
        });

---如果您完全确定指针集匹配,您可以作弊并即时创建全新的 unique_ptrs。--- 嗯,因为 const,就像您说的那样,它不起作用。

更新如果性能领先,只需删除 const。如果你知道对象不是实际上 const,你可以使用邪恶的const_cast&lt;&gt;

查看所有三种方法:Live on Coliru

#include <unordered_map>
#include <algorithm>
#include <memory>
#include <vector>
#include <cassert>

template <typename K, typename V>
using hash_map = std::unordered_map<K,V>;

struct X{};

int main()
{
    std::vector<std::unique_ptr<X>> uniq_ptrs;
    std::vector<const X*> const_ptrs;

    // naive approach, no performance worries
    auto idx_of = [&](X const* p) {
        return std::distance(begin(const_ptrs), std::find(begin(const_ptrs), end(const_ptrs), p)); 
    };
    std::sort(begin(uniq_ptrs), end(uniq_ptrs), [&](std::unique_ptr<X> const& a, std::unique_ptr<X> const& b) {
                return idx_of(a.get()) < idx_of(b.get());
            });

    // less naive approach, no performance worries
    hash_map<const X*, int> indices;
    for (auto i = 0u; i < const_ptrs.size(); ++i) {
        indices[const_ptrs[i]] = i;
    }

    std::sort(begin(uniq_ptrs), end(uniq_ptrs), [&](std::unique_ptr<X> const& a, std::unique_ptr<X> const& b) {
                return indices[ a.get() ] < indices[ b.get() ];
            });

    // cheating! only not UB if you know the objects aren't "physically" const
    assert(const_ptrs.size() == uniq_ptrs.size());
    for (auto i = 0u; i < const_ptrs.size(); ++i) {
        uniq_ptrs[i].release();
        uniq_ptrs[i].reset(const_cast<X*>(const_ptrs[i]));
    }
}

【讨论】:

  • 感谢您的回答。第二个代码 sn-p 是 O(n log n) 因为它排序,所以我相信它会比我的问题中的第二个代码 sn-p 慢。
  • @thesamet 我看不出你的就地版本是如何工作的(它不会编译,因为i 不在范围内,也不能交换)。但是,从概念上讲,无论如何您都在进行排序(这是算法的本质),并且在 hash_map 中也有隐式排序。我怀疑hash_map 插入时间将主导此处任何非平凡 n 的运行时。我建议您在 实际应用程序 中对此进行基准测试,看看它是否重要/什么有效。 删除 const 如果您需要它以提高性能。
  • 是的,我同意。尽管我没有尝试运行它,但我修复了我的第二个示例,所以它是正确的。
  • @thesamet 我添加了可疑的const_cast&lt;&gt; 版本,以防你真的想吃蛋糕。如果性能确实需要,我可以看到自己获得了过去的代码审查,并带有适当的注释文档和影响分析。
  • @sehe 我很困惑为什么const_cast 版本在这里是可疑的。我们知道(通过 OP 的断言)const_ptrsuniq_ptrs 包含相同的指针,因此不可能指向 const 对象。
猜你喜欢
  • 2019-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-01
  • 2020-08-12
  • 2021-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多