【问题标题】:std::set<std::pair<size_t, std::string> >::find(), without a string copy constructionstd::set<std::pair<size_t, std::string> >::find(),没有字符串复制构造
【发布时间】:2011-06-06 15:18:53
【问题描述】:

我有一个 std::set 的 std::pairs,第二对是一个字符串。我想检查集合中是否存在一对。

std::set<:pair std::string> > set_; bool 存在(size_t x, const std::string& s) { std::set >::iterator i = set_.find(std::make_pair(x, s)); // s 的副本由 make_pair 构造! 返回 i != set_.end(); }

我经常调用这个函数(是的,非常频繁),所以我想在不制作字符串的临时副本的情况下执行此检查。有没有办法做到这一点,它和我在这里所拥有的一样简单和简洁,但它不会制作字符串的临时副本?任何带有 STL 或 Boost 容器的解决方案都会很好。

【问题讨论】:

  • 你做错了。编写直截了当、干净且有效的代码。然后下线,当你完成后,profile你的应用程序,并优化结果说的慢;不要猜。
  • 这里有很多建议,我不应该担心这个函数的性能,直到我有证据表明这个函数会导致性能问题,我只想向阅读这篇文章的人提一下总的来说,这是非常好的建议。只是不是在这种特殊情况下。
  • 复制一个字符串并不像你想象的那么昂贵。当你证明它是一个瓶颈时,首先编写干净的代码优化后者。

标签: c++ stl


【解决方案1】:

你总是可以自己做。

static pair<size_t, std::string> helper(0,"");
typedef std::set< std::pair<size_t, std::string> >::iterator iterator_type;
helper.first = x;
for (iterator_type i = set_.lower_bound(helper); i != set_.end(); ++i) {
    if (i->first != x)
        return false;
    if (i->second == s)
        return true;
}
return false;

【讨论】:

  • 但与内置查找不同的是,它没有log(n) 查找时间。
  • @ltjax:我已经编辑了答案,以便它在log(n) 查找时间跳到具有正确 size_t 部分的部分。不过,该部分内的搜索仍然是线性的。如果每个 size_t 只有几个字符串,这将不明显。
【解决方案2】:

编写一个保持目标字符串引用的函子:

struct match_str : public std::unary_function<bool, std::string>
{
  match_str(const std::string& s) : s_(s) {};
  bool operator()(const std::pair<size_t, std::string>& rhs) const
  {
    return rhs.second == s_;
  }
};

用法:

std::set< std::pair<size_t, std::string> >::iterator i = std::find_if( set_.begin(), set_.end(), match_str(s) );

【讨论】:

  • 删除副本并通过O(n) 替换O(log n) 算法?我认为这会让事情变得更慢,而不是更快。
  • @ltjax:所以? @James Brock 要求提供不复制字符串的“任何解决方案”。这样做。
  • 常识!不要在优化问题中使算法变得更糟;-)
【解决方案3】:

不幸的是,如果不将 key_type 更改为类似引用的内容,您无法在 C++ 标准库中执行此操作。还有其他容器库具有模板参数化的查找功能,该功能允许不同的查找类型和比较器(例如 Boost.Intrusive)。除此之外,您只能希望优化器删除复制构造。 (基准!)

【讨论】:

    【解决方案4】:

    分析是否真的表明字符串副本在这里是一个重大问题?

    如果是这样,您是否能够更改 exists 函数,使其接受 pair 而不是两个参数,并安排将字符串直接构造成对而不是单独构造?

    如果你不能这样做,你总是可以使用shared_ptr&lt;std::string&gt; 作为pair 的第二个元素,并设计一个比较函数来比较来自地址的字符串而不是值字符串。

    【讨论】:

    • @rubenvb:如果unique_ptr 可用,无论如何。它依赖于右值引用,这实际上还没有在标准中,也没有在所有现代编译器中实现。
    • @David:是的,但在简单的用例中确实更好;)
    【解决方案5】:

    使用指向字符串的指针并覆盖谓词less(参见std::set的构造函数)

    【讨论】:

    • 我认为这可能是最好的答案。我正在使用 C++03(没有 C++0x r-value 参考功能,所以不能使用 unique_ptr),是的,我确实想保持 O(log n) 时间。这会很乱,但 C++ 很乱。
    • 这有一个不好的副作用,就是强制你手动管理存储在容器中的内存。在原始代码中,当 set 超出范围时,保存在字符串中的内存将被容器自动释放,通过将其更改为保存指针,set 析构函数将不会释放字符串。这将增加代码的其他部分的复杂性和脆弱性。
    猜你喜欢
    • 2011-06-04
    • 2012-02-06
    • 2021-09-15
    • 2011-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    • 1970-01-01
    相关资源
    最近更新 更多