函数模板和不明确的模板参数
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)
,key
是 std::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
可以隐式转换为 TKM
,Lookup
就可以使用 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 避免潜在的不必要的复制。
顺便说一句:C++20 支持 std::type_identity
;如果您的编译器不支持它,您可以轻松制作自己的编译器。
template<typename T> struct type_identity { typedef T type; };
我有几个 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)
,key
是 std::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
可以隐式转换为 TKM
,Lookup
就可以使用 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
引入另一个模板参数,如
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 避免潜在的不必要的复制。
顺便说一句:C++20 支持 std::type_identity
;如果您的编译器不支持它,您可以轻松制作自己的编译器。
template<typename T> struct type_identity { typedef T type; };