【问题标题】:C++ std::set custom comparatorC++ std::set 自定义比较器
【发布时间】:2013-05-02 12:00:33
【问题描述】:

好吧,我的问题是我正在使用带有自定义比较器的 std::set,例如:

class A
{
public:
    A(int x, int y):
        _x(x), _y(y)
    {
    }

    int hashCode(){ return (_y << 16) | _x; }

private:
    short int _y;
    short int _x;
};

struct comp
{
    bool operator() (A* g1, A* g2) const
    {
        return g1->hashCode() < g2->hashCode();
    }
};

所以,我喜欢使用它

std::set<A*, comp> myset;

// Insert some data
A* a = new A(2,1);
A* b = new A(1,3);
myset.insert(a);
myset.insert(b);

现在我的问题是我想这样做:

myset.find( (2 << 16) | 1 );

但是,当然,它除了 A* 而不是 short int。

所以,我知道我可以使用 std::find_if,但它不会使自定义比较器变得无用吗?它会迭代整个列表,不是吗?有什么方法可以将 find 用于 hashCode 而不是对象本身?

谢谢!

【问题讨论】:

  • 带比较器的 std::find_if 怎么样(调整相等)?
  • 对不起,这就是我写std::find时的意思,应该是std::find_if。它不会迭代整个列表,并且根本不优化搜索吗?我使用std::set 的原因是它的 O(log(n)) 搜索成本。

标签: c++ comparator stdset


【解决方案1】:

set::find 接受key_type 类型的参数(参见讨论Why is set::find not a template?)。使用 std::set 你必须构造一个临时对象来使用find

myset.find(A(2, 1));

如果 A 的构建成本不高,您可能希望使用 std::map&lt;int, A&gt;(或围绕它的包装器)。

【讨论】:

  • A 确实不便宜。如果我要使用地图,使用mapunordered_map 会更好吗?我目前正在使用 google::dense_hash_map 用于其他一些带有 int 键的地图。插入成本根本不重要,搜索成本才是。
  • @ProStage 如果订购无关紧要,您应该使用unordered_mapunordered_set
  • @JamesKanze 那A只是一个例子,不要写+100行类代码。 “问题是类没有那么简单。它实际上拥有一张地图,最多可以增长到 1000 个元素。A 对象的数量也可以增长到 1000+,它们都是独一无二的。” (来自另一条评论的 C&P)。它的构造函数无论如何都不便宜。
  • @hansmaad 从某种意义上说,我不介意存储的顺序并不重要,只要不增加搜索时间即可。我猜散列表是理想的
【解决方案2】:

std::set 不能这样做,因为 std::set&lt;&gt;::find 是 不是(成员)模板;参数必须是键类型。 对于像你这样的简单类,很可能使用 std::vector&lt;A&gt; 并保持排序(使用 std::lower_bound 用于查找,并作为插入点)将同样快。 使用std::lower_bound,你可以传入一个比较器, 使用您想要的任何类型作为键。您所要做的就是确保 您的 comp 类可以处理混合类型比较,例如:

struct Comp
{
    bool operator()( A const&, B const& ) const;
    bool operator()( A const&, int ) const;
    bool operator()( int, A const& ) const;
};

【讨论】:

  • 问题是类没有那么简单。它实际上拥有一张地图,可以增长到 1000 个元素。 A 对象的数量也可以增长到 1000+,它们都是独一无二的。搜索时间/成本很重要,矢量不是一个选项。此外,插入/擦除时会出现线程问题,因为所有迭代器都会失效。
  • @ProStage 然后你必须找到一个解决方法。 std::shared_ptr 的向量可能会起作用;或者,您可以尝试实现A 的“虚拟”版本,它的构建成本不高,并且可以用作std::set 的密钥。 (没有条目的map 构建起来并不昂贵。或者只是将实现或至少其昂贵的部分移动到委托中;然后可以将具有指向委托的空指针的实例创建为索引,并且如果委托是使用智能指针管理的,那么复制最终可能会非常便宜。)
【解决方案3】:
myset.find(&A(2, 1));

或者

A a(2, 1);
myset.find(&a);

【讨论】:

  • 第一个是不合法的C++。
  • 它不依赖于编译器。标准明确禁止它(除非类A 已覆盖operator&amp;);是编译器接受它(当作为 C++ 编译器调用时),它被破坏了。 (如果编译器接受它,我实际上会感到相当惊讶。毕竟,这条规则可以追溯到 C 的早期。但我以前对一些编译器接受的东西感到惊讶。)
【解决方案4】:

您已经定义了std::set&lt;A*, comp&gt; myset;,因此std::find() 必须采用A* 参数。

std::set<A*, comp> myset;

// Insert some data
A* a = new A(2,1);
A* b = new A(1,3);
myset.insert(a);
myset.insert(b);

那么,你需要做的

myset.find(&A(2,1))

回到您的问题,std::find() 没有采用您的自定义比较器。事实上,您需要使用std::find_if

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-07
    • 2010-11-06
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多