从异构列表中提取数据
Extract data from heterogeneous list
我看到了这个关于模板参数中自动的优点的答案。
请考虑以下异构列表:
template <auto ... vs> struct HeterogenousValueList {};
using MyList1 = HeterogenousValueList<42, 'X', 13u>;
现在我已经声明了一个名为MyList1
的类型。如何从该类型(即42、'x'或13u(中提取存储的数据?
要从模板参数包中提取数据,我们通常在模板中进行模式匹配。
首先,我们创建了一个没有内容的类模板At
。它的模板参数应该是一个索引和HeterogenousValueList
的一个实例。该类模板将像函数一样用于访问列表中的信息。
template <int Index, class ValueList>
struct At;
接下来,我们创建一个At
的专业化。这就是使用模式匹配的地方。通过模式匹配,列表的第一个元素将变为u
。列表的其余部分将是vs
。如果索引为0
,则可以通过静态成员value
访问u
。注意,vs
可以是一个空列表,因此这也涵盖了u
是列表的最后一个的情况。
template <auto u, auto... vs>
struct At<0, HeterogenousValueList<u, vs...>>
{
static constexpr auto value = u;
};
如果索引不是0怎么办?我们移动列表并将索引递减1,然后再次将它们传递到At
中。换句话说,这是一个模板递归。
template <int Index, auto u, auto... vs>
struct At<Index, HeterogenousValueList<u, vs...>>
{
static constexpr auto value = At<Index - 1, HeterogenousValueList<vs...>>::value;
};
现在,我们可以尝试使用它:https://godbolt.org/g/51dpH8
int main()
{
volatile auto value0 = At<0, MyList1>::value;
volatile auto value1 = At<1, MyList1>::value;
volatile auto value2 = At<2, MyList1>::value;
// volatile auto value3 = At<-1, MyList1>::value;
// volatile auto value4 = At<3, MyList1>::value;
}
我使用volatile变量,这样编译器就不会优化效果,您可以在程序集列表中看到效果。
还有一件很棒的事情:编译器检查绑定!出于运行时效率的原因,我们通常不会对运行时数组进行绑定检查。但这是一个编译时间列表。编译器可以为我们做这件事!
实际上,有一个更简单的实现。这一次,我们在函数模板中使用constexpr-if。但是模式匹配和模板递归的思想保持不变。
template <int Index, auto u, auto... vs>
auto at(HeterogenousValueList<u, vs...>)
{
if constexpr (Index == 0)
return u;
else
return at<Index - 1>(HeterogenousValueList<vs...>{});
}
这一次,当我们使用它时,我们需要将MyList1
实例化为一个对象。
https://godbolt.org/g/CA3VHj
int main()
{
volatile auto value0 = at<0>(MyList1{});
volatile auto value1 = at<1>(MyList1{});
volatile auto value2 = at<2>(MyList1{});
// volatile auto value3 = at<-1, MyList1>::value;
// volatile auto value4 = at<3, MyList1>::value;
}
当你提到"玩"answers"通过操纵来学习"时,你可能会对几种选择感兴趣。
第一种解决方案:转换为std::tuple
,然后转换为std::get
std::tuple
是异构值的容器,可用于访问异构值列表的元素。这是一个简单的两步过程:
- 将
HeterogenousValueList<42, 'X', 13u>{}
转换为std::tuple<int, char, unsigned>{42, 'X', 13u}
- 通过
std::get
访问所需位置的元组值
完整的C++17示例:
#include <type_traits>
#include <tuple>
template<auto... vs>
struct HeterogenousValueList {};
template<int i, auto... vs>
constexpr auto get(HeterogenousValueList<vs...>) {
constexpr std::tuple tuple{vs...};// class-template argument deduction
static_assert(std::is_same<
decltype(tuple), const std::tuple<int, char, unsigned>
>{});
return std::get<i>(tuple);
}
int main() {
using MyList1 = HeterogenousValueList<42, 'X', 13u>;
constexpr auto at1 = get<1>(MyList1{});
static_assert(at1 == 'X');
static_assert(std::is_same<decltype(at1), const char>{});
}
第二个解决方案:std::tuple_element_t
的封装类型
另一个有用的习惯用法是将非类型模板参数包装在单个空类型中。在您的示例中,这允许使用std::tuple_element_t
,它可以产生第n种类型的可变包:
#include <type_traits>
#include <tuple>
template<auto... vs>
struct HeterogenousValueList {};
template<auto v_>
struct SingleValue {// similar to std::integral_constant, but uses auto
static constexpr auto v = v_;
};
template<int i, auto... vs>
constexpr auto get(HeterogenousValueList<vs...>) {
using TupleOfSingleValues = std::tuple<SingleValue<vs>...>;
using SingleValueAtPosition = std::tuple_element_t<i, TupleOfSingleValues>;
return SingleValueAtPosition::v;
// return std::tuple_element_t<i, std::tuple<SingleValue<vs>...>>::v;// same
}
// same `main` as first solution
第三种解决方案:实现自己的逻辑
有几种方法可以实现您自己的"获取第n个类型/元素"版本。这种业务的一个方面是编译时性能:特别是递归策略据说会导致编译时间过长。
我最喜欢的非递归策略是Boost.Hana中也使用的策略。如果你喜欢视频解释,你可以从01小时12分钟开始观看Louis Dionne(Boost.Haa作者(的两分钟演讲"Metaprogramming for the brave"。这个想法是使用多重继承。在您的示例中,HeterogenousList<42, 'X', 13u>
可以具有基类IndexedValue<0, 42>
、IndexedValue<1, 'X'>
和IndexedValue<2, 13u>
。然后,您可以将HeterogenousList<42, 'X', 13u>
传递给模板化的get
函数,该函数以const IndexedValue<1, [[DEDUCED]]>&
为参数:
#include <type_traits>
#include <utility>
template<std::size_t i, auto v_>
struct IndexedValue {
static constexpr auto v = v_;
};
template<class Is, auto... vs>
struct IndexedHeterogenousList;
template<std::size_t... is, auto... vs>
struct IndexedHeterogenousList<
std::index_sequence<is...>,// partial specialization to extract the `is`
vs...
> : IndexedValue<is, vs>...// multiple "variadic" inheritance
{};
template<auto... vs>
struct HeterogenousValueList
: IndexedHeterogenousList<std::make_index_sequence<sizeof...(vs)>, vs...>
{};
template<std::size_t i, auto v>// `i` must be given; `v` is deduced
constexpr auto get(const IndexedValue<i, v>& iv) {// one base matches
return v;
}
// same `main` as first solution
- JPG-如何从ICC配置文件部分APP2读取/提取数据
- 如何从多维向量中提取数据
- 从异构列表中提取数据
- 从 Windows 中的其他应用程序 GUI 中提取数据
- 如何从 qwebengineview qt5.9 中提取数据
- 如何从shared_ptr<T&>的引用中提取数据
- 从自定义结构数组中提取数据
- 无法从结构数组 C++ 中提取数据
- 用字符串::擦除在C 中提取数据
- C 从结构Deque提取数据
- 如何从 char[] 字符串中提取数据
- 用C++从字符串中优雅地提取数据
- 如何从用|分隔的txt文件中提取数据
- 如何从结构数组中提取数据
- 如何使用 C++11 <thread> 设计从源中提取数据的系统
- 从数据库中提取数据时使用QVariant的正确方法
- 提取数据范围的分隔符
- 从文本文件中查找和提取数据
- 是否有用于从HTML页面中提取数据的库
- 使用Boost::python从嵌入式python中提取数据