函数模板和不明确的模板参数

Function template and ambigous template parameter

我有几个 std::map<some_type, some_other_type>,我正在尝试编写一个函数模板 Lookup,如下所示。

当键是指针或标量时函数模板工作正常,但如果键是std::string就会有问题。

#include <iostream>
#include <map>

// Usage :
//   bool valueisinmap = Lookup(themap, thekey, thevalue);
template <typename TK, typename TV>
bool Lookup(std::map<TK, TV>& map,  TK key, TV& value)
{
  auto it = map.find(key);
  if (it != map.end())
  {
    value = it->second;
    return true;
  }
  else
  {
    return false;
  }
}

int main()
{
    std::map<std::string, std::string> m;
    m.insert(std::make_pair("2", "two"));

    std::string x;
    std::string key = "2";

    if (Lookup(m, key, x))
      std::cout << "OK\n";

    if (Lookup(m, "2", x))  // problem here
      std::cout << "OK\n";
}

我明白为什么Lookup(m, "2", x)不能编译因为"2"的类型不是std::string但是有没有办法写函数模板所以我可以使用Lookup(m, "2", x) 以及 Lookup(m, key, x)keystd::string?

如果是,这会引发第二个问题:

bool Lookup(std::map<TK, TV>& map,  TK key, TV& value)

key 按值传递,如果 key 的类型是 std::string,则创建一个副本。有没有办法通过引用(或一些 C++14 和 plus 魔法)传递 key 并且仍然能够使用 Lookup(m, "2", x)?

解决这个问题的一种方法是为键类型引入一个单独的类型参数,如下所示:

template <typename TKM, typename TK, typename TV>
bool Lookup(const std::map<TKM, TV>& map, const TK& key, TV& value)
{
  auto it = map.find(key);
  if (it != map.end())
  {
    value = it->second;
    return true;
  }
  else
  {
    return false;
  }
}

只要 TK 可以隐式转换为 TKMLookup 就可以使用 TK 类型的键与具有键类型的映射一起调用TKM.

这里你需要两件事。首先,可以单独推导密钥类型(下面的typename K)。其次,您想将密钥作为 const 限定引用传递,并使用 C++14 透明比较函数(下面的 typename Comp)设置映射以避免不必要的复制(参见 this thread有关透明比较器的详细信息)。

template <typename TK, typename TV, typename Comp, typename K>
bool Lookup(const std::map<TK, TV, Comp>& map,  const K& key, TV& value)
{
   // Same as before...
}

std::map<std::string, std::string, std::less<>> m;

std::less<> 指定为 std::map 比较类型可确保 std::map::find 的重载 #3 和 #4 可用。

请注意,我另外 const 限定了地图参数本身,因为 Lookup 模板不会修改它。

您可以为 key 引入另一个模板参数,如 , another way is to exclude it from template argument deduction:

template <typename TK, typename TV>
bool Lookup(std::map<TK, TV>& map, const std::type_identity_t<TK>& key, TV& value)

那么TK只会从第一个参数map推导出来;如果你将 const char[] 作为 key 传递给函数,它将被转换为 std::string 然后作为参数传递。你可以使 is pass-by-const-reference 避免潜在的不必要的复制。

LIVE

顺便说一句:C++20 支持 std::type_identity;如果您的编译器不支持它,您可以轻松制作自己的编译器。

template<typename T> struct type_identity { typedef T type; };