通过自定义标准搜索集合<>

Searching a set<> via Custom Criterion

语言版本

我正在使用支持 C++17 的 GNU C++ 编译器版本(尽管我还不熟悉 C++11/14/17 中添加的所有内容)。

问题

在下面的代码中,我发现我可以使用自定义排序标准插入 set<>,但我无法使用 设置<>::查找()。调用 set<>::find():

时会产生以下编译错误

error: no matching function for call to ‘std::setstd::shared_ptr<node_t, node_comp_t>::find(char&) const’
return children.find(c) != children.end();

这让我感到困惑,因为元素等价由以下定义:

!(lhs < rhs) && !(rhs < lhs)

如下所示,我定义了所需的运算符。

不受欢迎的选择

作为替代方案,我可以逐个元素地搜索 set<>,但这并不可取,因为这样做会在线性时间内 运行 .

我的问题

  1. 我误会了什么?
  2. 有没有一种方法可以按对数时间进行搜索,就像通常使用 set<>::find()?

代码

struct node_t;
using node_sp_t = shared_ptr<node_t>;

class node_comp_t
{
   public:
      inline bool operator()(const node_sp_t &lhs, const node_sp_t &rhs) const;
};

using node_sp_set_t = set<node_sp_t, node_comp_t>;

class node_t
{
   public:
      explicit node_t(char c_p): c{c_p} {}

      inline char get_c() const
      {
         return c;
      }

      void add_child(char c)
      {
         if (!child_exists(c))
            children.insert(make_shared<node_t>(c));
      }

      bool child_exists(char c) const
      {
         // c is not of the children set element type (node_sp_t).
         // So, find() cannot be used with c.
         //
         // Why does node_comp_t not get used by find()?
         //
         // How may I search for c in logarithmic time if not by using find()?
         //
         return children.find(c) != children.end();

         // This works, but it runs in linear time!
         // for (const node_sp_t &node_sp : children)
         // {
         //    if (node_sp->get_c() == c)
         //       return true;
         // }

         // return false;
      }

   private:
      char c;
      node_sp_set_t children;
};

inline bool node_comp_t::operator()(const node_sp_t &lhs, const node_sp_t &rhs) const
{
   return lhs->get_c() < rhs->get_c();
}

你的比较器有两个 const node_sp_t& 类型的参数,所以你必须有一个 node_sp_t 对象来比较。

在 C++14 中,您可以使用 transparent comparator,允许您与不同的类型进行比较(在本例中,char

class node_comp_t
{
   public:
      inline bool operator()(char lhs, const node_sp_t &rhs) const;
      inline bool operator()(const node_sp_t &lhs, char rhs) const;
      inline bool operator()(const node_sp_t &lhs, const node_sp_t &rhs) const;
      using is_transparent = void;
};

inline bool node_comp_t::operator()(char lhs, const node_sp_t &rhs) const
{
   return lhs < rhs->get_c();
}
inline bool node_comp_t::operator()(const node_sp_t &lhs, char rhs) const
{
   return lhs->get_c() < rhs;
}
inline bool node_comp_t::operator()(const node_sp_t &lhs, const node_sp_t &rhs) const
{
   return lhs->get_c() < rhs->get_c();
}

但是因为您使用的是 C++11,所以这不是 std::set 的选项(您可以将它或类似的东西用于其他实现,例如 boost::container::set)。

一种无需动态分配就可以廉价创建 shared_ptr 的“简单”方法是使用别名构造函数:

bool child_exists(char c) const
{
    node_t compare_against{c};
    return children.find(node_sp_t{node_sp_t{nullptr}, &compare_against}) != children.end();
}

不幸的是,你必须支付在 C++11 中构建 node_t 的成本。

如果默认构造很昂贵,您可能还想在 add_child 中从这里移动:

void add_child(char c)
{
   node_t compare_against{c};
   if (children.find(node_sp_t{node_sp_t{nullptr}, &compare_against}) == children.end())
      children.insert(std::make_shared<node_t>(std::move(compare_against)));
}

或者,您可以使用 std::map<char, node_sp_t> 之类的东西。