2

我有 avector<std::unique_ptr<X>>和 avector<const X*>包含相同的指针集,但顺序不同。我想让唯一指针的向量与 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]]]);
}

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

4

2 回答 2

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
于 2013-09-12T21:57:04.467 回答
0

如果性能没有问题:

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<>

查看所有三种方法:住在 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]));
    }
}
于 2013-09-12T21:52:55.807 回答