用不同的密钥类型重载std :: map

时间:2018-08-01 13:43:43

标签: c++ stl

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

#include <map>
#include <vector>
// ...
class Type
{
 std::string key;
 int support;
};
std::map<Type, std::vector<int> > index;

我想重载地图,因此下面的两个if子句都可以工作:

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;
}

我尝试了这些重载:

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;
    }
  };

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

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

3 个答案:

答案 0 :(得分:7)

从C ++ 14开始,您需要的被称为异构查找,并且受std::map支持。为此,您可以定义自己的比较器结构并在其中提供类型is_transparent以启用此功能。有关如何启用异构查询的详细信息,请参见How can I search an std::map using a key of a different type

还请注意,尽管std::map::find()确实支持std::map::operator[],所以您必须替换代码:

if (index.find("test") != index.end())
{
   index["test"].push_back(0);
}

类似于:

 auto it = index.find("test");
 if( it != index.end()) 
     it->second.push_back(0);

无论如何,您都应该这样做,因为您将执行两次查找而不是一次查找,这在地图上的操作非常昂贵。

所以对于您的情况,比较器应类似于:

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; }
};

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

std::map<Type, std::vector<int>,CompareType> index;

并且您不再需要Type本身中的比较方法

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

答案 1 :(得分:0)

只需尝试以下方法:

std::map<Type, std::vector<int>, std::less<> > index;

这需要C ++ 14。请注意,使用没有模板参数的std::less可以搜索“兼容”键,而不仅仅是精确的key_type。这就是您需要通过自定义比较功能来“解锁” find()的使用。

std::map的默认第三个模板参数为std::less<key_type>,这是受限制的。为了与C ++ 14之前的代码向后兼容,仍然使用这种方法,但是如果没有模板参数就可以使用std::less来完成许多新代码。

答案 2 :(得分:0)

我在提供的代码中看到两个问题:

  1. Type类不支持小于操作,当用作std :: map <>键类型时,这是必需的。
  2. 在需要使用Type的两个地方使用字符串(char数组常量):map::find()map::operator[]的参数。编译器无法从const char *转换为Type。

首先,满足Type作为std :: map <>:

的键类型的要求
// Define comparison/ordering of two Type instances
bool operator<(const Type& a, const Type& b)
{
   return a.key < b.key;
}

第二,定义如何使用single-arg ctor将const char *(例如“ test”)隐式转换为Type:

class Type 
{
public:
   // Implicitly converts const char* value to instance of Type.
   Type(const char* k) : key(k), support(0) {}
   //...
};

这不需要任何更新的(C ++ 11或C ++ 14)语言功能。