如何使用 C++ 模板高效地展开序列
How to efficiently roll out a sequence using c++ templates
我有一个复杂类型 C
取决于我在(长度有界)序列中需要的模板参数。 constexpr 函数 next()
可用于从 C_n -> C_n+1。由于每个序列元素都有不同的类型,我使用 std::tuple
来存储结果。 mkTuple()
函数负责(有限的)序列推出。
这是我所做的一个简化示例(使用 std::array
作为我更复杂的 C 的占位符):
#include <array>
#include <tuple>
#include <iostream>
template<std::size_t OFFSET>
using C=std::array<uint8_t, OFFSET>;
static
constexpr
std::size_t
next(const std::size_t START, const std::size_t DISTANCE)
{
return START + DISTANCE;
}
template<template<std::size_t OFFSET> class CONTENT, std::size_t START, std::size_t DISTANCE, std::size_t SEQ_LENGTH>
struct mkTuple
{
using _LT = CONTENT<START>;
using _RT = typename mkTuple<CONTENT, next(START, DISTANCE), DISTANCE, SEQ_LENGTH - 1>::tuple;
using tuple=decltype(std::tuple_cat(std::make_tuple(_LT()), _RT()));
};
template<template<std::size_t OFFSET> class CONTENT, std::size_t START, std::size_t DISTANCE>
struct mkTuple<CONTENT, START, DISTANCE, 1>
{
using _LT = CONTENT<START>;
using tuple = std::tuple<_LT>;
};
int
main()
{
using tMyTuple = typename mkTuple<C, 16, 2, 64>::tuple;
const std::size_t iTupleLength = std::tuple_size<tMyTuple>::value;
std::cout << "Sequence length = " << iTupleLength << std::endl;
const tMyTuple myTuple;
std::cout << "Tuple[4].size() = " << std::get<4>(myTuple).size() << std::endl;
return 0;
}
问题是我的 mkTuple()
函数似乎非常占用内存,以至于我很快 运行 陷入编译器内存不足的麻烦。事实上 g++ -ftime-report ...
报告模板实例化的使用量超过 1GB。随着序列长度的增加,这个数字迅速(呈指数级?)上升。
我怀疑 std::tuple_cat
所需的 std::tuple
实例化是充气器。有人知道如何更有效地推出序列吗?
提前致谢!
似乎 std::tuple
对您的场景来说有点矫枉过正 - 您只能在类型系统级别上工作,然后在最后转换为 std::tuple
。
由于std::tuple
的实现又大又复杂,所以对于大量的实例化,它会很慢也就不足为奇了。滚动你自己的 typelist
:
template <typename... Ts>
struct typelist
{
using as_tuple = std::tuple<Ts...>;
};
template <typename... As, typename... Bs>
constexpr auto typelist_cat(typelist<As...>, typelist<Bs...>)
-> typelist<As..., Bs...>
{
return {};
}
然后替换std::tuple
的用法:
template<template<std::size_t OFFSET> class CONTENT, std::size_t START, std::size_t DISTANCE, std::size_t SEQ_LENGTH>
struct mkTuple
{
using _LT = CONTENT<START>;
using _RT = typename mkTuple<CONTENT, START + DISTANCE, DISTANCE, SEQ_LENGTH - 1>::tuple;
using tuple = decltype(typelist_cat(typelist<_LT>{}, _RT{}));
};
template<template<std::size_t OFFSET> class CONTENT, std::size_t START, std::size_t DISTANCE>
struct mkTuple<CONTENT, START, DISTANCE, 1>
{
using _LT = CONTENT<START>;
using tuple = typelist<_LT>;
};
最后在最后进行转换:
using tMyTuple = typename mkTuple<C, 16, 2, 64>::tuple::as_tuple;
输出:
Sequence length = 64
Tuple[4].size() = 24
我有一个复杂类型 C
取决于我在(长度有界)序列中需要的模板参数。 constexpr 函数 next()
可用于从 C_n -> C_n+1。由于每个序列元素都有不同的类型,我使用 std::tuple
来存储结果。 mkTuple()
函数负责(有限的)序列推出。
这是我所做的一个简化示例(使用 std::array
作为我更复杂的 C 的占位符):
#include <array>
#include <tuple>
#include <iostream>
template<std::size_t OFFSET>
using C=std::array<uint8_t, OFFSET>;
static
constexpr
std::size_t
next(const std::size_t START, const std::size_t DISTANCE)
{
return START + DISTANCE;
}
template<template<std::size_t OFFSET> class CONTENT, std::size_t START, std::size_t DISTANCE, std::size_t SEQ_LENGTH>
struct mkTuple
{
using _LT = CONTENT<START>;
using _RT = typename mkTuple<CONTENT, next(START, DISTANCE), DISTANCE, SEQ_LENGTH - 1>::tuple;
using tuple=decltype(std::tuple_cat(std::make_tuple(_LT()), _RT()));
};
template<template<std::size_t OFFSET> class CONTENT, std::size_t START, std::size_t DISTANCE>
struct mkTuple<CONTENT, START, DISTANCE, 1>
{
using _LT = CONTENT<START>;
using tuple = std::tuple<_LT>;
};
int
main()
{
using tMyTuple = typename mkTuple<C, 16, 2, 64>::tuple;
const std::size_t iTupleLength = std::tuple_size<tMyTuple>::value;
std::cout << "Sequence length = " << iTupleLength << std::endl;
const tMyTuple myTuple;
std::cout << "Tuple[4].size() = " << std::get<4>(myTuple).size() << std::endl;
return 0;
}
问题是我的 mkTuple()
函数似乎非常占用内存,以至于我很快 运行 陷入编译器内存不足的麻烦。事实上 g++ -ftime-report ...
报告模板实例化的使用量超过 1GB。随着序列长度的增加,这个数字迅速(呈指数级?)上升。
我怀疑 std::tuple_cat
所需的 std::tuple
实例化是充气器。有人知道如何更有效地推出序列吗?
提前致谢!
似乎 std::tuple
对您的场景来说有点矫枉过正 - 您只能在类型系统级别上工作,然后在最后转换为 std::tuple
。
由于std::tuple
的实现又大又复杂,所以对于大量的实例化,它会很慢也就不足为奇了。滚动你自己的 typelist
:
template <typename... Ts>
struct typelist
{
using as_tuple = std::tuple<Ts...>;
};
template <typename... As, typename... Bs>
constexpr auto typelist_cat(typelist<As...>, typelist<Bs...>)
-> typelist<As..., Bs...>
{
return {};
}
然后替换std::tuple
的用法:
template<template<std::size_t OFFSET> class CONTENT, std::size_t START, std::size_t DISTANCE, std::size_t SEQ_LENGTH>
struct mkTuple
{
using _LT = CONTENT<START>;
using _RT = typename mkTuple<CONTENT, START + DISTANCE, DISTANCE, SEQ_LENGTH - 1>::tuple;
using tuple = decltype(typelist_cat(typelist<_LT>{}, _RT{}));
};
template<template<std::size_t OFFSET> class CONTENT, std::size_t START, std::size_t DISTANCE>
struct mkTuple<CONTENT, START, DISTANCE, 1>
{
using _LT = CONTENT<START>;
using tuple = typelist<_LT>;
};
最后在最后进行转换:
using tMyTuple = typename mkTuple<C, 16, 2, 64>::tuple::as_tuple;
输出:
Sequence length = 64
Tuple[4].size() = 24