如何在编译时推断出嵌套 std::vector 的内部类型?
How can I deduce the inner type of a nested std::vector at compile time?
前几天我非常 问了一个关于嵌套向量的问题,但我遇到了另一个让我难过的问题。我需要在编译时获取嵌套向量的最内层类型,以便我可以使用它作为模板参数传递。
例如,如果我有这个嵌套向量:
std::vector<std::vector<std::vector<int>>> v;
我需要一种提取 int
的方法,这样我就可以调用一个采用嵌套向量并处理元素的函数,如下所示:
foo<int>(v);
除了要注意的是,此函数应该能够处理包含任何类型的任何深度的嵌套向量。当我调用 foo
时,我希望自动为我推导出内部类型。
所以调用可能看起来像这样:
foo<inner_type_t<v>>(v);
其中 inner_type_t
是某种形式的递归模板,在给定 v
.
时解析为 int
我认为解决方案与另一个问题的解决方案类似,但我一直无法解决...在递归模板方面我还是个新手。
编辑:
这是我目前所掌握的...
template <typename T>
struct inner_type
{
using type = T;
};
template <typename T>
struct inner_type<std::vector<T>>
{
using type = inner_type<T>;
};
int main()
{
std::vector<std::vector<int>> v = {
{ 1, 2}, {3, 4}
};
std::cout << typeid(inner_type<decltype(v)>::type).name();
}
输出:
struct inner_type<class std::vector<int,class std::allocator<int> > >
哇,我真的很接近哈哈,成功了!
我只需要稍微更改模板专业化即可正确地递归获取类型。
template <typename T>
struct inner_type
{
using type = T;
};
template <typename T>
struct inner_type<std::vector<T>>
{
// Had to change this line
using type = typename inner_type<T>::type;
};
int main()
{
std::vector<std::vector<int>> v = {
{ 1, 2}, {3, 4}
};
std::cout << typeid(inner_type<decltype(v)>::type).name();
}
输出:
int
您可以定义以下主要 class 模板,inner_type
:
template<typename T>
struct inner_type {
using type = T;
};
用作基本情况,即用于停止递归——当模板参数不匹配时std::vector<T>
(见下文)。
然后,以下方便的别名模板仅用于编写类似 C++14 的尾随 _t
而不是 ::type
:
template<typename T>
using inner_type_t = typename inner_type<T>::type;
最后,std::vector<T>
的特化 – 递归情况:
template<typename T>
struct inner_type<std::vector<T>> {
using type = inner_type_t<T>;
};
当传递 std::vector<T>
作为模板参数时,此特化匹配。否则,将匹配第一个(见上文)。
检查一下。您可以声明以下 class 模板:
template<typename> struct type_shower;
然后:
auto main() -> int {
using type = inner_type_t<std::vector<std::vector<int>>>;
type_shower<type> _;
}
它应该显示一个错误,指出未定义模板的隐式实例化 type_shower<int>
。这意味着 type
是 int
.
遵循 Bulletmagnet 建议使用 value_type
成员类型的解决方案:
template<class T, typename = void>
struct inner_type {
using type = T;
};
template<class T>
struct inner_type<T, std::void_t<typename T::value_type>>
: inner_type<typename T::value_type> {};
template<class T>
using inner_type_t = typename inner_type<T>::type;
using VV = std::vector<std::vector<int>>;
static_assert(std::is_same_v<inner_type_t<VV>, int>);
可以在 this question 中找到关于 std::void_t
工作原理的非常好的解释。如果 typename T::value_type
格式不正确,它在这里用于静默拒绝特化。
@tjwrona1992 的解决方案没问题,但不允许向量具有不同的分配器。此外,让我们使用特征的 _t
版本使这个 C++14 友好。
这应该可以解决问题:
template <typename T> struct inner_type { using type = T; };
template<class T, class Alloc>
struct inner_type<std::vector<T, Alloc>> { using type = typename inner_type<T>::type; };
template<class T>
using inner_type_t = typename inner_type<T>::type;
此外,对于类型名称,您应该使用为 C++17 here for C++14 or here 实现的 type_name()
函数。
这是一个更通用的解决方案。它也适用于 list
和 forward_list
,而不仅仅是 vector
。
#include <vector>
#include <list>
#include <forward_list>
#include <type_traits>
//using nested = std::vector<std::vector<std::vector<int>>>;
using nested = std::list<std::vector<std::forward_list<double>>>;
// primary template handles types that have no nested value_type member:
template< class, class = std::void_t<> >
struct has_vt : std::false_type { };
// specialization recognizes types that do have a nested value_type member:
template< class T >
struct has_vt<T, std::void_t<typename T::value_type>> : std::true_type { };
template <typename T, typename Enable = void> struct inner;
template <typename T> struct inner<T, typename std::enable_if<!has_vt<T>::value>::type> {
using vt = T;
};
template <typename T> struct inner<T, typename std::enable_if<has_vt<T>::value>::type> {
using vt = typename inner<typename T::value_type>::vt;
};
template<typename> struct type_shower;
int main() {
using deeep = inner<nested>::vt;
type_shower<deeep> _;
return 0;
}
Boost.Mp11,这是一个简短的单行(一如既往)
template <typename T>
using inner_value_t = mp_last<mp_iterate<T, mp_identity_t, mp_first>>;
Demo.
这依赖于你只关心vector
,而vector<T, A>
的值类型只是T
。这实际上适用于所有序列容器(这适用于 list
、deque
、forward_list
等,很好。虽然对于 vector<map<int, double>>
它会给你 int
).
如果我们有 vector<vector<char>>
,mp_iterate
将首先生成序列 mp_list<vector<vector<char>>, vector<char>, char>
(应用 mp_first
直到我们不能再应用,然后将结果传递给 mp_identity_t
这只是一个空操作)。然后 mp_last
returns 该列表中的最后一个类型。这是我们想要的类型:char
.
如果您想扩展以支持任意范围,您可以使用 std::ranges::range_value_t
(C++20) 而不是 mp_first
,这是最通用的解决方案。
或者,如果您只关心嵌套的 value_type
别名:
template <typename T> using value_type_t = typename T::value_type;
template <typename T>
using inner_value_t = mp_last<mp_iterate<T, mp_identity_t, value_type_t>>;
前几天我非常
例如,如果我有这个嵌套向量:
std::vector<std::vector<std::vector<int>>> v;
我需要一种提取 int
的方法,这样我就可以调用一个采用嵌套向量并处理元素的函数,如下所示:
foo<int>(v);
除了要注意的是,此函数应该能够处理包含任何类型的任何深度的嵌套向量。当我调用 foo
时,我希望自动为我推导出内部类型。
所以调用可能看起来像这样:
foo<inner_type_t<v>>(v);
其中 inner_type_t
是某种形式的递归模板,在给定 v
.
int
我认为解决方案与另一个问题的解决方案类似,但我一直无法解决...在递归模板方面我还是个新手。
编辑:
这是我目前所掌握的...
template <typename T>
struct inner_type
{
using type = T;
};
template <typename T>
struct inner_type<std::vector<T>>
{
using type = inner_type<T>;
};
int main()
{
std::vector<std::vector<int>> v = {
{ 1, 2}, {3, 4}
};
std::cout << typeid(inner_type<decltype(v)>::type).name();
}
输出:
struct inner_type<class std::vector<int,class std::allocator<int> > >
哇,我真的很接近哈哈,成功了!
我只需要稍微更改模板专业化即可正确地递归获取类型。
template <typename T>
struct inner_type
{
using type = T;
};
template <typename T>
struct inner_type<std::vector<T>>
{
// Had to change this line
using type = typename inner_type<T>::type;
};
int main()
{
std::vector<std::vector<int>> v = {
{ 1, 2}, {3, 4}
};
std::cout << typeid(inner_type<decltype(v)>::type).name();
}
输出:
int
您可以定义以下主要 class 模板,inner_type
:
template<typename T>
struct inner_type {
using type = T;
};
用作基本情况,即用于停止递归——当模板参数不匹配时std::vector<T>
(见下文)。
然后,以下方便的别名模板仅用于编写类似 C++14 的尾随 _t
而不是 ::type
:
template<typename T>
using inner_type_t = typename inner_type<T>::type;
最后,std::vector<T>
的特化 – 递归情况:
template<typename T>
struct inner_type<std::vector<T>> {
using type = inner_type_t<T>;
};
当传递 std::vector<T>
作为模板参数时,此特化匹配。否则,将匹配第一个(见上文)。
检查一下。您可以声明以下 class 模板:
template<typename> struct type_shower;
然后:
auto main() -> int {
using type = inner_type_t<std::vector<std::vector<int>>>;
type_shower<type> _;
}
它应该显示一个错误,指出未定义模板的隐式实例化 type_shower<int>
。这意味着 type
是 int
.
遵循 Bulletmagnet 建议使用 value_type
成员类型的解决方案:
template<class T, typename = void>
struct inner_type {
using type = T;
};
template<class T>
struct inner_type<T, std::void_t<typename T::value_type>>
: inner_type<typename T::value_type> {};
template<class T>
using inner_type_t = typename inner_type<T>::type;
using VV = std::vector<std::vector<int>>;
static_assert(std::is_same_v<inner_type_t<VV>, int>);
可以在 this question 中找到关于 std::void_t
工作原理的非常好的解释。如果 typename T::value_type
格式不正确,它在这里用于静默拒绝特化。
@tjwrona1992 的解决方案没问题,但不允许向量具有不同的分配器。此外,让我们使用特征的 _t
版本使这个 C++14 友好。
这应该可以解决问题:
template <typename T> struct inner_type { using type = T; };
template<class T, class Alloc>
struct inner_type<std::vector<T, Alloc>> { using type = typename inner_type<T>::type; };
template<class T>
using inner_type_t = typename inner_type<T>::type;
此外,对于类型名称,您应该使用为 C++17 here for C++14 or here 实现的 type_name()
函数。
这是一个更通用的解决方案。它也适用于 list
和 forward_list
,而不仅仅是 vector
。
#include <vector>
#include <list>
#include <forward_list>
#include <type_traits>
//using nested = std::vector<std::vector<std::vector<int>>>;
using nested = std::list<std::vector<std::forward_list<double>>>;
// primary template handles types that have no nested value_type member:
template< class, class = std::void_t<> >
struct has_vt : std::false_type { };
// specialization recognizes types that do have a nested value_type member:
template< class T >
struct has_vt<T, std::void_t<typename T::value_type>> : std::true_type { };
template <typename T, typename Enable = void> struct inner;
template <typename T> struct inner<T, typename std::enable_if<!has_vt<T>::value>::type> {
using vt = T;
};
template <typename T> struct inner<T, typename std::enable_if<has_vt<T>::value>::type> {
using vt = typename inner<typename T::value_type>::vt;
};
template<typename> struct type_shower;
int main() {
using deeep = inner<nested>::vt;
type_shower<deeep> _;
return 0;
}
Boost.Mp11,这是一个简短的单行(一如既往)
template <typename T>
using inner_value_t = mp_last<mp_iterate<T, mp_identity_t, mp_first>>;
Demo.
这依赖于你只关心vector
,而vector<T, A>
的值类型只是T
。这实际上适用于所有序列容器(这适用于 list
、deque
、forward_list
等,很好。虽然对于 vector<map<int, double>>
它会给你 int
).
如果我们有 vector<vector<char>>
,mp_iterate
将首先生成序列 mp_list<vector<vector<char>>, vector<char>, char>
(应用 mp_first
直到我们不能再应用,然后将结果传递给 mp_identity_t
这只是一个空操作)。然后 mp_last
returns 该列表中的最后一个类型。这是我们想要的类型:char
.
如果您想扩展以支持任意范围,您可以使用 std::ranges::range_value_t
(C++20) 而不是 mp_first
,这是最通用的解决方案。
或者,如果您只关心嵌套的 value_type
别名:
template <typename T> using value_type_t = typename T::value_type;
template <typename T>
using inner_value_t = mp_last<mp_iterate<T, mp_identity_t, value_type_t>>;