是否有支持严格大小检查的唯一值的C ++容器?

时间:2017-12-13 11:23:06

标签: c++ c++11 stl containers

我正在寻找一个C ++容器来存储指向对象的指针,这些指针也符合以下要求。

  • 保持元素顺序的容器(序列容器,因此std::set不合适)
  • 具有返回实际大小的成员函数的容器(As std::array::size()始终返回固定大小,std::array不合适)
  • 支持随机访问的容器,例如operator []

这是我的代码片段,我想删除用于检查元素大小和唯一性的断言。

#include <vector>
#include <set>
#include "assert.h"

class Foo {
  public:
    void DoSomething() {
    }
};

int main() {
  // a variable used to check whether a container is properly assigned
  const uint8_t size_ = 2; 

  Foo foo1;
  Foo foo2;

  // Needs a kind of sequential containers to keep the order
  // used std::vector instead of std::array to use member function size()
  const std::vector<Foo*> vec = {
    &foo1,
    &foo2
  };

  std::set<Foo*> set_(vec.begin(), vec.end()); 
  assert(vec.size() == size_); // size checking against pre-defined value
  assert(vec.size() == set_.size()); // check for elements uniqueness

  // Needs to access elements using [] operator
  for (auto i = 0; i < size_; i++) {
    vec[i]->DoSomething();
  }

  return 0;
}

是否有一个C ++容器,不需要在我的代码片段中使用两个断言?或者我应该创建自己的类来封装一个STL容器吗?

2 个答案:

答案 0 :(得分:0)

这样一个类似于向量的类,除非你插入,它拒绝像集合或映射这样的重复项。

一个选项可能是Boost.Bimap,索引为T *和sequence_index。 你的矢量索引将通过sequence_index。在删除元素后,您甚至可能愿意接受序列中的漏洞。

坚持使用STLyou可以使用2个地图实现双向地图,或者下面使用地图和矢量:

请注意,通过继承vector,我可以免费获得所有的矢量方法,但我也冒险将用户向下转换为矢量。
没有使用包装器(la queue vs list)进行重新构造的一种方法是使其成为受保护的继承,然后显式地将所有方法重新公开。这实际上更安全,因为它可以确保您不会无意中留下一些矢量修改方法,这会使两个容器失去一步。

另请注意,如果您希望过滤掉任何重复项,则需要滚动自己的initializer_list构造函数。而且你必须做一些工作才能使这个线程安全。

template <class T>
class uniqvec : public std::vector<T*>
{
private:
    typedef typename std::vector<T*> Base;
    enum {push_back, pop_back, emplace_back, emplace}; //add anything else you don't like from vector
std::map <T*, size_t> uniquifier;
public:

    std::pair<typename Base::iterator, bool> insert(T* t)
    {
        auto rv1 = uniquifier.insert(std::make_pair(t, Base::size()));
        if (rv1.second)
        {
            Base::push_back(t);
        }
        return std::make_pair(Base::begin()+rv1.first.second, rv1.second);
    }
    void erase(T* t)
    {
        auto found = uniquifier.find(t);
        if (found != uniquifier.end())
        {
            auto index = found->second;
            uniquifier.erase(found);
            Base::erase(Base::begin()+index);
            for (auto& u : uniquifier)
                if (u.second > index)
                    u.second--;
        }
    }
    // Note that c++11 returns the next safe iterator,
    // but I don't know if that should be in vector order or set order.
    void erase(typename Base::iterator i)
    {
        return erase(*i);
    }
};

答案 1 :(得分:0)

正如其他人所提到的那样,你的特定问题似乎就像XY问题一样(关于特定解决方案的杂草,而不是关注原始问题)。几年前提供了一个非常有用的流程图(归功于@MikaelPersson),它将帮助您选择最适合您需求的特定STL容器。您可以在In which scenario do I use a particular STL container?找到原始问题。 C++11 Standard Template Library Flowchart