如何将参数包转换成 std::tuple 以外的东西?
How to transform Parameter Pack into something else than std::tuple?
最好举例说明:
template <typename T1, typename T2>
struct OnePair
{
using TupleOfArgs = std::tuple<T1, T2>;
using TupleOfPairs = std::tuple<std::pair<T1, T2>>;
};
template <typename T1, typename T2, typename T3, typename T4>
struct TwoPairs
{
using TupleOfArgs = std::tuple<T1, T2, T3, T4>;
using TupleOfPairs = std::tuple<std::pair<T1, T2>, std::pair<T3, T4>>;
};
template <typename... Args>
struct NPairs
{
using TupleOfArgs = std::tuple<Args...>;
// using TupleOfPairs = ???
};
OnePair 定义了一对元组。
TwoPairs 定义了一个包含两对的元组。
如何在 NPairs 中定义 TupleOfPairs 以便将参数包转换为 std::tuple 对?
是否可以使用 std 库来实现?也许 boost::mpl?
两个答案,都很棒。
@chris 使用迭代方法,而@aschepler 使用递归解决方案。
就个人而言,我发现递归解决方案更容易理解。
您可以使用熟悉的索引序列技巧,但大小减半 (live example):
static constexpr auto get_tuple() {
constexpr auto N = sizeof...(Args);
static_assert(N%2 == 0);
using ArgsTuple = std::tuple<Args...>;
auto impl = []<std::size_t... Is>(std::index_sequence<Is...>) {
return std::tuple<
// Is goes from 0 to N/2, representing Ith pair
std::pair<std::tuple_element_t<Is*2, ArgsTuple>, std::tuple_element_t<Is*2 + 1, ArgsTuple>>...
>{};
};
return impl(std::make_index_sequence<N/2>{});
}
using TupleOfArgs = decltype(get_tuple());
如果您没有 C++20,则必须将 lambda 扩展为一个函数,而不是能够使用很好的内联索引序列扩展,但核心仍然可以运行。使用类型列表而不是 std::tuple
可能会更简洁一些,但它是可行的。
这是使用递归助手的一种直接方法:
template <typename PairsTuple, typename... Ts>
struct ZipPairs;
template <typename PairsTuple>
struct ZipPairs<PairsTuple> { using type = PairsTuple; };
template <typename... Pairs, typename T1, typename T2, typename... Ts>
struct ZipPairs<std::tuple<Pairs...>, T1, T2, Ts...>
{
using type = typename ZipPairs<
std::tuple<Pairs..., std::pair<T1, T2>>, Ts...>::type;
};
template <class... Args>
struct NPairs
{
static_assert(sizeof...(Args) % 2 == 0);
using TupleOfArgs = std::tuple<Args...>;
using TupleOfPairs = typename ZipPairs<std::tuple<>, Args...>::type;
};
最好举例说明:
template <typename T1, typename T2>
struct OnePair
{
using TupleOfArgs = std::tuple<T1, T2>;
using TupleOfPairs = std::tuple<std::pair<T1, T2>>;
};
template <typename T1, typename T2, typename T3, typename T4>
struct TwoPairs
{
using TupleOfArgs = std::tuple<T1, T2, T3, T4>;
using TupleOfPairs = std::tuple<std::pair<T1, T2>, std::pair<T3, T4>>;
};
template <typename... Args>
struct NPairs
{
using TupleOfArgs = std::tuple<Args...>;
// using TupleOfPairs = ???
};
OnePair 定义了一对元组。 TwoPairs 定义了一个包含两对的元组。
如何在 NPairs 中定义 TupleOfPairs 以便将参数包转换为 std::tuple 对?
是否可以使用 std 库来实现?也许 boost::mpl?
两个答案,都很棒。 @chris 使用迭代方法,而@aschepler 使用递归解决方案。 就个人而言,我发现递归解决方案更容易理解。
您可以使用熟悉的索引序列技巧,但大小减半 (live example):
static constexpr auto get_tuple() {
constexpr auto N = sizeof...(Args);
static_assert(N%2 == 0);
using ArgsTuple = std::tuple<Args...>;
auto impl = []<std::size_t... Is>(std::index_sequence<Is...>) {
return std::tuple<
// Is goes from 0 to N/2, representing Ith pair
std::pair<std::tuple_element_t<Is*2, ArgsTuple>, std::tuple_element_t<Is*2 + 1, ArgsTuple>>...
>{};
};
return impl(std::make_index_sequence<N/2>{});
}
using TupleOfArgs = decltype(get_tuple());
如果您没有 C++20,则必须将 lambda 扩展为一个函数,而不是能够使用很好的内联索引序列扩展,但核心仍然可以运行。使用类型列表而不是 std::tuple
可能会更简洁一些,但它是可行的。
这是使用递归助手的一种直接方法:
template <typename PairsTuple, typename... Ts>
struct ZipPairs;
template <typename PairsTuple>
struct ZipPairs<PairsTuple> { using type = PairsTuple; };
template <typename... Pairs, typename T1, typename T2, typename... Ts>
struct ZipPairs<std::tuple<Pairs...>, T1, T2, Ts...>
{
using type = typename ZipPairs<
std::tuple<Pairs..., std::pair<T1, T2>>, Ts...>::type;
};
template <class... Args>
struct NPairs
{
static_assert(sizeof...(Args) % 2 == 0);
using TupleOfArgs = std::tuple<Args...>;
using TupleOfPairs = typename ZipPairs<std::tuple<>, Args...>::type;
};