Heterogeneous Lookup

https://www.cppstories.com/2021/heterogeneous-access-cpp20/

C++ 14 加入了对有序容器的异质查找

用户的工作量很小,对标准库中的类型,只需要加上第三个模板参数:std::less<>(它的默认参数是 void)。std::less<void> 类中申明了 is_transparent 类型,所以可以用于异质查找。

std::map<std::string, int> intMap {
    { "Hello Super Long String", 1 },
    { "Another Longish String", 2 },
    { "This cannot fall into SSO buffer", 3 }
};

std::cout << "Lookup in intMap with by const char*:\\n";
std::cout << intMap.contains("Hello Super Long String") << '\\n';
//
std::map<std::string, int, std::less<>> trIntMap {
    { "Hello Super Long String", 1 },
    { "Another Longish String", 2 },
    {"This cannot fall into SSO buffer", 3 }
};

std::cout << "Lookup in trIntMap by const char*: \\n";
std::cout << trIntMap.contains("Hello Super Long String") << '\\n';

C++ 20 加入了对无序容器的异质查找

需要提供标注 is_transparent 的 hash 算子和等于算子。等于算子一般可以直接用 std::equal_to<>,但 hash 算子常常需要我们自己提供。

struct string_hash {
  using is_transparent = void;
  [[nodiscard]] size_t operator()(const char *txt) const {
    // 对指针的hash只考虑了地址,所以要选用string_view的hash
    return std::hash<std::string_view>{}(txt);
  }
  [[nodiscard]] size_t operator()(std::string_view txt) const {
    return std::hash<std::string_view>{}(txt);
  }
  [[nodiscard]] size_t operator()(const std::string &txt) const {
    // string和string_view的hash方式一样
    // 但用string的特化会减少一次从string到string_view的构造
    return std::hash<std::string>{}(txt);
  }
};

std::unordered_map<std::string, int, string_hash, std::equal_to<>>
      intMapTransparent {
    { "Hello Super Long String", 1 },
    { "Another Longish String", 2 },
    {"This cannot fall into SSO buffer", 3 }
};

bool found = intMapTransparent.contains("Hello Super Long String");
std::cout << "Found: " << std::boolalpha << found << '\\n';

为什么 C++ 不把异质容器的查找作为默认?

同质查找是先转换类型,再多次比较。异质查找是多次比较,每次比较时调用的算子可能会做类型转换操作。如果算子没有对异质查找优化,每次比较时都构建临时对象开销非常大。

标准库的 string / string_view / const char * 是一个很好的可以使用异质查找的例子,因为他们之间的相互比较都有良好的无需临时构造对象的操作符。