使用不同的键类型重载std :: map

Min*_*dge 2 c++ stl

所以我有以下STL std::map容器

#include <map>
#include <vector>
// ...
class Type
{
 std::string key;
 int support;
};
std::map<Type, std::vector<int> > index;
Run Code Online (Sandbox Code Playgroud)

我想重载地图,所以如果下面的条款工作:

int main()
{
  std::map<Type, std::vector<int> > index;
  Type type1;
  type1.key = "test";
  type1.support = 10;
  index[type1] = std::vector<int>();
  if (index.find(type1) != index.end())
  {
    index[type1].push_back(0);
  }
  // I can not make this work
  if (index.find("test") != index.end())
  {
    index["test"].push_back(0);
  }


  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我试过这些重载:

class Type 
{
 public:
  std::string key;
  int support;
  size_t operator()() const
  {
    return std::hash<std::string>{}(name);
  }

  bool operator==(const struct Type& obj) const 
  {
    return (key == obj.key);
  }

  bool operator<(const struct Type& obj) const 
  {
    return key < obj.key;
  }

  bool operator<(const std::string& other_key) const 
  {
    return key < other_key;
  }


  bool operator==(const std::string& other_key) const
  {
    return other_key == key;

  }


};

namespace std
{

  template<>
  struct hash<Type>
  {
    size_t operator()(const Type& obj) const
    {
      return obj();
    }
    // Specialization here does not seem to work
    size_t operator()(const std::string& name) const
    {
      return std::hash<std::string>{}(name);
    }
  };

  template<>
  struct less<Type>
  {

    bool operator() (const std::string& lname, const std::string& rname)
    {
      return lname < rname;
    }
  };
Run Code Online (Sandbox Code Playgroud)

因为在我的模型中,std::string key字段唯一地定义了Type如何重载std::map容器以便可以索引容器的项目?我能用C++做到吗?

PS:我知道一些代码在重载中可能是多余的

Sla*_*ica 7

您需要的是异构查找,并且std::map自C++ 14以来一直支持.为此,您可以定义自己的比较器结构并is_transparent在其中提供类型以启用此功能.有关如何启用异构查找的详细信息,请参见此处如何使用其他类型的键搜索std :: map

还要注意,虽然std::map::find()支持它std::map::operator[]没有,所以你必须替换你的代码:

if (index.find("test") != index.end())
{
   index["test"].push_back(0);
}
Run Code Online (Sandbox Code Playgroud)

类似于:

 auto it = index.find("test");
 if( it != index.end()) 
     it->second.push_back(0);
Run Code Online (Sandbox Code Playgroud)

无论如何你应该这样做,因为你会做两次查找,而不是其他一次,这在地图上是非常昂贵的操作.

所以对于你的情况,比较器应该是这样的:

struct CompareType
{
    using is_transparent = std::true_type;

    // standard comparison (between two instances of Type)
    bool operator()(const Type& lhs, const Type& rhs) const { return lhs.key < rhs.key; }
    // comparisons btw Type and std::string
    bool operator()( const Type& lhs, const std::string &rhs) const { return lhs.key < rhs; }
    bool operator()(const std::string &lhs, const Type& rhs) const { return lhs < rhs.key; }
};
Run Code Online (Sandbox Code Playgroud)

然后你通过以下方式创建地图:

std::map<Type, std::vector<int>,CompareType> index;
Run Code Online (Sandbox Code Playgroud)

你不再需要比较方法Type

另一种方法是提交std::less<>作为第三个参数std::map,但在这种情况下你缺少比较运算符,它std::string作为左操作数和Type右操作,并且该比较运算符不能成为Type我的成员我认为通过一个单独的比较器做它更干净(代码有一致).