提问人:HarryP2023 提问时间:8/18/2023 最后编辑:HarryP2023 更新时间:8/18/2023 访问量:107
约束嵌套 std::map 的键和值内部类型
Constrain the key and value inner type of a nested std::map
问:
最初,我想将嵌套的内部类型限制为,例如,int 或 std::string。我能够使用 C++20 的概念和以下堆栈溢出票证来实现这一点:如何获取 n 嵌套向量的最内部类型?std::vector
// trait to get the innermost type
template <typename T>
struct inner_type
{
using type = T;
};
// vector specialization
template <typename T>
struct inner_type<std::vector<T>>
{
using type = typename inner_type<T>::type;
};
// alias for convenience
template<typename T>
using inner_type_t = typename inner_type<T>::type;
template<typename T>
concept IsInnerTypeInt = (std::same_as<inner_type_t<T>, int>);
template<typename Arg>
requires IsInnerTypeInt<Arg>
auto bar(Arg arg)
{
}
int main() {
std::vector<int> intvec{1, 2, 3};
std::vector<std::vector<int>> intvec_vec{{1,2}, {3, 4}};
bar(intvec);
bar(intvec_vec);
}
目前为止,一切都好。当我想为嵌套的 std::map 添加专用化以约束键和值的内部类型时,问题就来了:
// map value specialization
template<typename Key, typename Val>
struct inner_type<std::map<Key, Val>>
{
using type = typename inner_type<Val>::type;
};
// map key specialization
template<typename Key, typename Value>
struct inner_type<std::map<Key, Value>>
{
using type = typename inner_type<Key>::type;
};
这样做,我得到一个重新定义错误,因为函数签名是相同的。我怎样才能做到这一点?我已经尝试过使用模板模板参数,但老实说,我不确定这是否是正确的路径。
答:
你不需要做这样的装置来推断容器的内部类型(元素类型)。
所有标准容器都有一个特性。对于关联容器(例如 ),您还有 和 。::value_type
std::map
::key_type
::mapped_type
您可以在此基础上使用概念。
请注意,是 (!std::map<K, V>
value_type
std::pair<K const, V>
const
我不是很擅长概念,但这抓住了这个想法:
#include<type_traits>
#include<string>
#include<map>
template<typename Map>
requires std::is_same_v<typename Map::value_type, std::pair<int const, std::string>>
auto bar(Map const& arg) {}
int main() {
std::map<int, std::string> m;
bar(m); // ok
std::map<int, double> n;
bar(n); // not ok
}
https://godbolt.org/z/W9xb4W5TG
澄清后添加的注释:如果你需要接受嵌套容器类型,你仍然可以使用该技术,但如果你想确保中间容器是或至少是-like,我会更多地依赖。key_type
mapped_type
std::map
std::map
例如,对于以下情况,它可以是:vector<map<map<int, vector<int>>, vector<int>>>
template<typename NestedContainer>
requires (
std::is_same_v<typename NestedContainer::value_type::key_type::key_type, int>
&& std::is_same_v<typename NestedContainer::value_type::key_type::mapped_type::value_type, int>
&& std::is_same_v<typename NestedContainer::value_type::mapped_type::value_type, int>
)
auto bar(NestedContainer const& arg) {}
int main() {
std::vector<std::map<std::map<int, std::vector<int>>, std::vector<int>>> c;
bar(c); // ok
std::map<int, double> d;
bar(d); // not ok
}
https://godbolt.org/z/nqYzvfx63
评论
vector<map<map<int, vector<int>>, vector<int>>>
value_type
most_nested_value
您可以约束专用化 和 具有相同的内部类型,并将内部类型定义为std::map
Key
Val
template<typename Key, typename Val>
requires std::same_as<typename inner_type<Key>::type,
typename inner_type<Val>::type>
struct inner_type<std::map<Key, Val>>
{
using type = typename inner_type<Val>::type;
};
评论
您遇到的重新定义错误是因为您尝试为同一模板类 std::map 专门化inner_type结构两次。不幸的是,C++ 不允许对类模板的单个成员进行部分专用化,而这正是您正在尝试执行的操作。
您可以通过引入一个新特征来处理嵌套类型,然后将该特征专门用于 std::vector 和 std::map,而不是以两种不同的方式专门化 std::map 的inner_type,而是实现您的目标。这是你如何做到的:
#include <vector>
#include <map>
#include <type_traits>
// trait to get the innermost type
template <typename T>
struct inner_type {
using type = T;
};
// vector specialization
template <typename T>
struct inner_type<std::vector<T>> {
using type = typename inner_type<T>::type;
};
// map value specialization
template <typename Key, typename Val>
struct inner_type<std::map<Key, Val>> {
using type = typename inner_type<Val>::type;
};
// map key specialization
template <typename Key, typename Value>
struct inner_type<std::pair<Key, Value>> {
using type = typename inner_type<Key>::type;
};
template<typename T>
using inner_type_t = typename inner_type<T>::type;
template<typename T>
concept IsInnerTypeInt = std::same_as<inner_type_t<T>, int>;
template<typename Arg>
requires IsInnerTypeInt<Arg>
auto bar(Arg arg) {
// Your implementation here
}
int main() {
std::vector<int> intvec{1, 2, 3};
std::vector<std::vector<int>> intvec_vec{{1,2}, {3, 4}};
std::map<std::string, int> map_str_int{{"one", 1}, {"two", 2}};
std::map<int, std::string> map_int_str{{1, "one"}, {2, "two"}};
bar(intvec);
bar(intvec_vec);
// bar(map_str_int); // This will not compile due to the constraint
// bar(map_int_str); // This will not compile due to the constraint
return 0;
}
在此代码中,新的 inner_type<std::p air<Key, Value>> 专用化处理提取 std::map 键的内部类型的情况,从而避免重新定义整个inner_type结构。
请注意,我已经用 std::map 参数注释掉了对 bar 的调用,因为你有一个只允许内部类型的 int 的约束。如果要在映射中处理不同类型的内部值,则需要为这些情况创建其他约束或重载。
评论
std::map<X, int>
int
评论
bar()
int
std::vector<int>
std::vector<std::vector<int>>
value_type
std::map
std::vector
value_type
bar
std::map<int, double>