约束嵌套 std::map 的键和值内部类型

Constrain the key and value inner type of a nested std::map

提问人:HarryP2023 提问时间:8/18/2023 最后编辑:HarryP2023 更新时间:8/18/2023 访问量:107

问:

最初,我想将嵌套的内部类型限制为,例如,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;
};

这样做,我得到一个重新定义错误,因为函数签名是相同的。我怎样才能做到这一点?我已经尝试过使用模板模板参数,但老实说,我不确定这是否是正确的路径。

C++ 20 标准矢量 标准地图 C++-概念

评论

0赞 Toby Speight 8/18/2023
这种“内在”类型有什么用?很难看出你为什么需要它。为什么对 、 、 等有用,而对其他类型没有用?bar()intstd::vector<int>std::vector<std::vector<int>>
0赞 463035818_is_not_an_ai 8/18/2023
你的问题似乎是一个奇怪的“inner_type”概念。如果你坚持使用,那么它的工作方式与它的工作方式完全相同,因为它们都有一个成员别名value_typestd::mapstd::vectorvalue_type
0赞 HarryP2023 8/18/2023
我已经在问题中澄清了,它一定是嵌套地图。例如 std::map<std::vector<int>、int> 或 std::map<int、std::vector<int>> 或 std::vector<std::map<int、std::vector<std::map<int、int>>>>
0赞 Caleth 8/18/2023
应该接受还是拒绝?您将需要处理一系列类型,而不是单个类型。barstd::map<int, double>

答:

1赞 alfC 8/18/2023 #1

你不需要做这样的装置来推断容器的内部类型(元素类型)。

所有标准容器都有一个特性。对于关联容器(例如 ),您还有 和 。::value_typestd::map::key_type::mapped_type

您可以在此基础上使用概念。

请注意,是 (!std::map<K, V>value_typestd::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_typemapped_typestd::mapstd::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

评论

2赞 康桓瑋 8/18/2023
我相信 OP 要求工作。vector<map<map<int, vector<int>>, vector<int>>>
0赞 HarryP2023 8/18/2023
正确。在您的示例中,这不起作用:std::map<int, std::vector<std::string>> q;酒吧(q);它必须接受任意嵌套的向量和映射
0赞 HarryP2023 8/18/2023
我已经在问题中澄清了它必须是嵌套的 std::map 和 std::vector。以下是获取嵌套向量类型的链接: stackoverflow.com/questions/30960715/...
0赞 alfC 8/18/2023
哦,我明白;事物可以任意嵌套。嗯,是的,你可以把它替换成什么的。但是,祝你好运,编写在所有情况下都有效的函数的实现。value_typemost_nested_value
3赞 康桓瑋 8/18/2023 #2

您可以约束专用化 和 具有相同的内部类型,并将内部类型定义为std::mapKeyVal

 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;
  };

评论

0赞 HarryP2023 8/18/2023
我喜欢这个解决方案,但是是否可以扩展这个解决方案,以便 Key 和 Value 可以具有不同的内部类型?例如,这行不通: std::map<int, std::vector<std::string>> m;bar(米);
0赞 Ahmadkhan 8/18/2023 #3

您遇到的重新定义错误是因为您尝试为同一模板类 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 的约束。如果要在映射中处理不同类型的内部值,则需要为这些情况创建其他约束或重载。

评论

2赞 康桓瑋 8/18/2023
我不明白这是如何工作的,它接受任何 X 不是 .std::map<X, int>int