在 C++ 中推导模板 return 类型
Deduce template return type in C++
目前我尝试编写一个函数 retrieveKeys()
,它给我一个 std::map
的键并将它存储在一些 std::container
中。该函数应在两种方面具有通用性:
- 接受
std::map
和 std::unordered_map
作为参数类型。
- Return 用户定义容器中的键,例如。 G。
std::vector
或 std::deque
(容器必须支持 push_back()
方法)。
目前该功能的使用方式如下:
std::unordered_map<int, int> testMap;
std::map<int, int> testMap2;
std::vector<int> keys1 = retrieveKeys<std::vector>(testMap);
std::deque<int> keys2 = retrieveKeys<std::deque>(testMap);
std::vector<int> keys3 = retrieveKeys<std::vector>(testMap2);
std::deque<int> keys4 = retrieveKeys<std::deque>(testMap2);
具有以下功能:
template<template<typename...> typename KeyContainer, typename... KeyContainer_Rest,
template<typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest>
inline KeyContainer<K, KeyContainer_Rest...> retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map)
{
KeyContainer<K, KeyContainer_Rest...> keys;
for (const auto& m : map)
{
keys.push_back(m.first);
}
return keys;
}
如果我不必显式地编写 return 类型就好了。但是当我尝试像
这样的东西时
std::vector<int> keys1_ = retrieveKeys(testMap);
/*
error: no matching function for call to 'retrieveKeys'
std::vector<int> keys1_ = retrieveKeys(testMap);
^~~~~~~~~~~~
*/
使用 clang 3.6 (C++17) 编译时出现上述错误。
所以我的问题是:是否可以重写函数,使 return 类型可以被编译器减少?
这里再放一遍完整的代码,方便复制:
#include <deque>
#include <vector>
#include <unordered_map>
#include <map>
template<template<typename...> typename KeyContainer, typename... KeyContainer_Rest,
template<typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest>
inline KeyContainer<K, KeyContainer_Rest...> retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map)
{
KeyContainer<K, KeyContainer_Rest...> keys;
for (const auto& m : map)
{
keys.push_back(m.first);
}
return keys;
}
int main()
{
std::unordered_map<int, int> testMap;
std::map<int, int> testMap2;
std::vector<int> keys1 = retrieveKeys<std::vector>(testMap);
std::deque<int> keys2 = retrieveKeys<std::deque>(testMap);
std::vector<int> keys3 = retrieveKeys<std::vector>(testMap2);
std::deque<int> keys4 = retrieveKeys<std::deque>(testMap2);
//std::vector<int> keys1_ = retrieveKeys(testMap);
/*
error: no matching function for call to 'retrieveKeys'
std::vector<int> keys1_ = retrieveKeys(testMap);
^~~~~~~~~~~~
*/
}
没有。编译器无法判断 return 类型应该是什么,因为它没有可用于确定的信息(您可以在立即将其分配给变量的上下文之外调用 retrieveKeys()
你想要的类型)。
但是,您可以使用 auto
:
来减少代码重复
auto keys1 = retrieveKeys<std::vector>(testMap);
auto keys2 = retrieveKeys<std::deque>(testMap);
auto keys3 = retrieveKeys<std::vector>(testMap2);
auto keys4 = retrieveKeys<std::deque>(testMap2);
不,在调用站点 没有return 类型推导,因为编译器会缺少必要的上下文。与例如比较std::make_unique
:
auto derived_ptr = std::make_unique<Derived>(args); // have to specify return type
一般来说,模板参数推导适用于提供的模板参数。提供一个输出参数将推导出一切
template<class InputParam, class OutputParam>
void copy(InputParam const& src, OutParam& dst) { /* bla */ }
// call as:
InputParam src = /* fill */;
OutputParam dst; // empty
copy(src, dst) // template arguments deduced from supplied src, dst
相反,如果没有它,您必须显式提供模板参数:
template<class InputParam, class OutputParam>
OutputParam copy(InputParam const& src) { OutputParam x; /* bla */ return x; }
// call as:
InputParam src = /* fill */;
auto dst = copy<OutputParam>(src); // InputParam deduced from src, supply OutputParam template argument
相比之下,在定义点,C++14确实有return类型推导,所以你可以写
template<template<typename...> typename KeyContainer, typename... KeyContainer_Rest,
template<typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest>
auto // <-- here
retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map)
{
KeyContainer<K, KeyContainer_Rest...> keys;
for (const auto& m : map)
{
keys.push_back(m.first);
}
return keys; // compiler will deduce return-type of retrieveKeys from this
}
template <typename K, typename M>
struct ReturnTypeDeducer
{
const M& map;
ReturnTypeDeducer(const M& m) : map(m) {}
template <template <typename...> typename KeyContainer, typename... KeyContainer_Rest>
operator KeyContainer<K, KeyContainer_Rest...>() &&
{
KeyContainer<K, KeyContainer_Rest...> keys;
for (const auto& m : map)
{
keys.push_back(m.first);
}
return keys;
}
};
template <template <typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest>
inline ReturnTypeDeducer<K, MapContainer<K, V, MapContainer_Rest...>> retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map)
{
return map;
}
int main()
{
std::unordered_map<int, int> testMap;
std::map<int, int> testMap2;
std::vector<int> keys1 = retrieveKeys(testMap);
std::deque<int> keys2 = retrieveKeys(testMap);
std::vector<int> keys3 = retrieveKeys(testMap2);
std::deque<int> keys4 = retrieveKeys(testMap2);
}
An example of how you can use container::key_type
template <class CONTAINER>
std::vector <typename CONTAINER::key_type>
retrieveKeys(CONTAINER container)
{
std::vector <typename CONTAINER::key_type> keys;
for (auto itr : container)
{
keys.push_back(itr.first);
}
return keys;
}
int _tmain(int argc, _TCHAR* argv[])
{
typedef std::map <int, int> MYMAP;
MYMAP values;
values.insert(std::make_pair(1, 1));
values.insert(std::make_pair(2, 2));
values.insert(std::make_pair(3, 3));
typedef std::vector <typename MYMAP::key_type> KEYVECTOR;
KEYVECTOR keys = retrieveKeys<MYMAP>(values);
for (auto itr : keys)
std::cout << itr std::endl;
}
目前我尝试编写一个函数 retrieveKeys()
,它给我一个 std::map
的键并将它存储在一些 std::container
中。该函数应在两种方面具有通用性:
- 接受
std::map
和std::unordered_map
作为参数类型。 - Return 用户定义容器中的键,例如。 G。
std::vector
或std::deque
(容器必须支持push_back()
方法)。
目前该功能的使用方式如下:
std::unordered_map<int, int> testMap;
std::map<int, int> testMap2;
std::vector<int> keys1 = retrieveKeys<std::vector>(testMap);
std::deque<int> keys2 = retrieveKeys<std::deque>(testMap);
std::vector<int> keys3 = retrieveKeys<std::vector>(testMap2);
std::deque<int> keys4 = retrieveKeys<std::deque>(testMap2);
具有以下功能:
template<template<typename...> typename KeyContainer, typename... KeyContainer_Rest,
template<typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest>
inline KeyContainer<K, KeyContainer_Rest...> retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map)
{
KeyContainer<K, KeyContainer_Rest...> keys;
for (const auto& m : map)
{
keys.push_back(m.first);
}
return keys;
}
如果我不必显式地编写 return 类型就好了。但是当我尝试像
这样的东西时std::vector<int> keys1_ = retrieveKeys(testMap);
/*
error: no matching function for call to 'retrieveKeys'
std::vector<int> keys1_ = retrieveKeys(testMap);
^~~~~~~~~~~~
*/
使用 clang 3.6 (C++17) 编译时出现上述错误。
所以我的问题是:是否可以重写函数,使 return 类型可以被编译器减少?
这里再放一遍完整的代码,方便复制:
#include <deque>
#include <vector>
#include <unordered_map>
#include <map>
template<template<typename...> typename KeyContainer, typename... KeyContainer_Rest,
template<typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest>
inline KeyContainer<K, KeyContainer_Rest...> retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map)
{
KeyContainer<K, KeyContainer_Rest...> keys;
for (const auto& m : map)
{
keys.push_back(m.first);
}
return keys;
}
int main()
{
std::unordered_map<int, int> testMap;
std::map<int, int> testMap2;
std::vector<int> keys1 = retrieveKeys<std::vector>(testMap);
std::deque<int> keys2 = retrieveKeys<std::deque>(testMap);
std::vector<int> keys3 = retrieveKeys<std::vector>(testMap2);
std::deque<int> keys4 = retrieveKeys<std::deque>(testMap2);
//std::vector<int> keys1_ = retrieveKeys(testMap);
/*
error: no matching function for call to 'retrieveKeys'
std::vector<int> keys1_ = retrieveKeys(testMap);
^~~~~~~~~~~~
*/
}
没有。编译器无法判断 return 类型应该是什么,因为它没有可用于确定的信息(您可以在立即将其分配给变量的上下文之外调用 retrieveKeys()
你想要的类型)。
但是,您可以使用 auto
:
auto keys1 = retrieveKeys<std::vector>(testMap);
auto keys2 = retrieveKeys<std::deque>(testMap);
auto keys3 = retrieveKeys<std::vector>(testMap2);
auto keys4 = retrieveKeys<std::deque>(testMap2);
不,在调用站点 没有return 类型推导,因为编译器会缺少必要的上下文。与例如比较std::make_unique
:
auto derived_ptr = std::make_unique<Derived>(args); // have to specify return type
一般来说,模板参数推导适用于提供的模板参数。提供一个输出参数将推导出一切
template<class InputParam, class OutputParam>
void copy(InputParam const& src, OutParam& dst) { /* bla */ }
// call as:
InputParam src = /* fill */;
OutputParam dst; // empty
copy(src, dst) // template arguments deduced from supplied src, dst
相反,如果没有它,您必须显式提供模板参数:
template<class InputParam, class OutputParam>
OutputParam copy(InputParam const& src) { OutputParam x; /* bla */ return x; }
// call as:
InputParam src = /* fill */;
auto dst = copy<OutputParam>(src); // InputParam deduced from src, supply OutputParam template argument
相比之下,在定义点,C++14确实有return类型推导,所以你可以写
template<template<typename...> typename KeyContainer, typename... KeyContainer_Rest,
template<typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest>
auto // <-- here
retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map)
{
KeyContainer<K, KeyContainer_Rest...> keys;
for (const auto& m : map)
{
keys.push_back(m.first);
}
return keys; // compiler will deduce return-type of retrieveKeys from this
}
template <typename K, typename M>
struct ReturnTypeDeducer
{
const M& map;
ReturnTypeDeducer(const M& m) : map(m) {}
template <template <typename...> typename KeyContainer, typename... KeyContainer_Rest>
operator KeyContainer<K, KeyContainer_Rest...>() &&
{
KeyContainer<K, KeyContainer_Rest...> keys;
for (const auto& m : map)
{
keys.push_back(m.first);
}
return keys;
}
};
template <template <typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest>
inline ReturnTypeDeducer<K, MapContainer<K, V, MapContainer_Rest...>> retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map)
{
return map;
}
int main()
{
std::unordered_map<int, int> testMap;
std::map<int, int> testMap2;
std::vector<int> keys1 = retrieveKeys(testMap);
std::deque<int> keys2 = retrieveKeys(testMap);
std::vector<int> keys3 = retrieveKeys(testMap2);
std::deque<int> keys4 = retrieveKeys(testMap2);
}
An example of how you can use container::key_type
template <class CONTAINER>
std::vector <typename CONTAINER::key_type>
retrieveKeys(CONTAINER container)
{
std::vector <typename CONTAINER::key_type> keys;
for (auto itr : container)
{
keys.push_back(itr.first);
}
return keys;
}
int _tmain(int argc, _TCHAR* argv[])
{
typedef std::map <int, int> MYMAP;
MYMAP values;
values.insert(std::make_pair(1, 1));
values.insert(std::make_pair(2, 2));
values.insert(std::make_pair(3, 3));
typedef std::vector <typename MYMAP::key_type> KEYVECTOR;
KEYVECTOR keys = retrieveKeys<MYMAP>(values);
for (auto itr : keys)
std::cout << itr std::endl;
}