英文:
How to zip own structure using template metaprogramming
问题
I have my list structure and metafunctions for it.
template<int ...>
class MyList;
template<int H, int ... T>
class MyList<H, T ...> {
public:
static const int Head = H;
using Tail = MyList<T ...>;
};
template<>
class MyList<> {};
//list length
template<typename TL>
class Length {
public:
static const int value = 1 + Length<typename TL::Tail>::value;
};
template<>
class Length<MyList<>> {
public:
static const int value = 0;
};
//Add an element to the front
template<int H, typename IL>
class IntCons;
template<int H, int... Tail>
class IntCons<H, MyList<Tail...>> {
public:
using type = MyList<H, Tail...>;
};
//Generate a list with N elements
template <int N, int ... Next>
class Generate : public Generate<N-1, N-1, Next...> { };
template <int ... Next>
struct Generate<0, Next ... > {
using type = MyList<Next ... >;
};
I want to implement something like std::transform
. I wrote the metafunction Calculate
. It takes 2 of my lists as parameters and a binary metafunction, and it must take each element from each list and call my binary metafunction for this pair.
Binary metafunction:
template<int x, int y>
struct Multiply {
static int const value = x * y;
};
Calculate metafunction:
template<typename L1, typename L2, template<int, int> class MetaF, class... Tail>
struct Calculate;
template<typename L1, typename L2, template<int, int> class MetaF, int... Tail>
struct Calculate : Calculate<typename L1::Tail, typename L2::Tail, MetaF, Tail...> {
static_assert(Length<L1>::value == Length<L2>::value, "The arguments of lists should be of the same size");
using type = IntCons<MetaF<L1::Head, L2::Head>::value, Tail...>;
};
If L1 = MyList<2, 2, 3, 3, 5>
and L2 = MyList<5, 3, 8, 7, 3>
, I am expecting the result MyList<10, 6, 24, 21, 15>
. But I have an error parameter packs not expanded with ‘...’
. Tell me please how to fix it?
英文:
I have my list structure and metafunctions for it.
template<int ...>
class MyList;
template<int H, int ... T>
class MyList<H, T ...> {
public:
static const int Head = H;
using Tail = MyList<T ...>;
};
template<>
class MyList<> {};
//list lenght
template<typename TL>
class Length {
public:
static const int value = 1 + Length<typename TL::Tail>::value;
};
template<>
class Length<MyList<>> {
public:
static const int value = 0;
};
//Add an element to front
template<int H, typename IL>
class IntCons;
template<int H, int... Tail>
class IntCons<H, MyList<Tail...>>{
public:
using type = MyList<H, Tail...>;
};
//Generate list with N elements
template <int N, int ... Next>
class Generate : public Generate<N-1, N-1, Next...> { };
template <int ... Next>
struct Generate<0, Next ... > {
using type = MyList<Next ... >;
};
I want to implement something as std::transform
. I wrote metafunction Calculate
. It takes 2 my lists as parameters an binary metafunction and it must take each element from each list and call my binary metafunction for this pair.
Binary metafunction
template<int x, int y>
struct Multiply {
static int const value = x * y;
};
Calculate metafunction
template<typename L1, typename L2, template<int, int> class MetaF, class... Tail>
struct Calculate;
template<typename L1, typename L2, template<int, int> class MetaF, class... Tail>
struct Calculate : Calculate<typename L1::Tail, typename L2::Tail, MetaF, Tail...> {
static_assert( Length<L1>::value == Length<L2>::value,
"The arguments of lists should be of the same size" );
using type = IntCons<MetaF<L1::Head, L2::Head>::value, Tail...>;
};
If L1 = MyList<2,2,3,3,5>
and L2 = MyList<5,3,8,7,3>
I am waiting result MyList<10, 6, 24, 21, 15>
But I have an error parameter packs not expanded with ‘...’
Tell me please how to fix it?
答案1
得分: 3
我的建议是忘记递归。对于模板元编程,使用递归是老旧且复杂的方式。在大多数情况下,这不再是必要的。我不得不承认,你的代码对我来说太复杂了。你只需要列表、元函数、部分特化来推断从给定实例化(例如MyList<2, 2>
)中推断出MyList
,然后将其插回,以及一个为了方便而设置的_t
别名:
#include <iostream>
#include <type_traits>
template<int ...> class MyList;
template<int x, int y> struct Multiply {
static int const value = x * y;
};
template<typename L1, typename L2, template<int, int> class MetaF>
struct Calculate;
template <template <int,int> class MetaF, template<int...> class ML, int... I1, int... I2>
struct Calculate<ML<I1...>, ML<I2...>, MetaF> {
using type = ML< MetaF<I1, I2>::value ...>;
};
template <typename L1, typename L2, template<int, int> class MetaF>
using Calculate_t = typename Calculate<L1, L2, MetaF>::type;
int main() {
using L1 = MyList<2, 2>;
using L2 = MyList<5, 3>;
using R = MyList<10, 6>;
std::cout << std::is_same_v<R, Calculate_t<L1, L2, Multiply>>;
}
输出:
1
另外,如果你确实需要推断列表的长度,可以使用部分特化和sizeof...
运算符:
#include <iostream>
#include <type_traits>
template<int ...> class MyList;
template <class T> struct Length;
template <template <int...> class L, int... I>
struct Length<L<I...>> {
static const size_t value = sizeof...(I);
};
template <class T> constexpr size_t Length_v = Length<T>::value;
int main() {
using L1 = MyList<2, 2>;
std::cout << Length_v<L1>;
}
然而,更简单的替代方法是在MyList
中有一个constexpr size_t length = sizeof...(I)
成员。上面的代码只在MyList
本身没有这样的成员时才是必要的。
英文:
My tip: Forget about recursion. Using recursion for template metaprogramming is the old complicated way. It is not necessary in most cases anymore. I have to admit, that your code is too complicated for me to comprehend. All you need is the list, the metafunction, partial specialization to infer MyList
from a given instantiation (eg MyList<2,2>
), then plug it back in, and for convenience a _t
alias:
#include <iostream>
#include <type_traits>
template<int ...> class MyList;
template<int x, int y> struct Multiply {
static int const value = x * y;
};
template<typename L1, typename L2, template<int, int> class MetaF>
struct Calculate;
template <template <int,int> class MetaF,template<int...> class ML,int... I1,int... I2>
struct Calculate< ML<I1...>,ML<I2...>,MetaF> {
using type = ML< MetaF<I1,I2>::value ...>;
};
template <typename L1,typename L2, template<int,int> class MetaF>
using Calculate_t = typename Calculate<L1,L2,MetaF>::type;
int main() {
using L1 = MyList<2,2>;
using L2 = MyList<5,3>;
using R = MyList<10,6>;
std::cout << std::is_same_v<R, Calculate_t<L1,L2,Multiply>>;
}
1
PS: If you do need to infer the length of the list, use partial specialization and the sizeof...
operator:
#include <iostream>
#include <type_traits>
template<int ...> class MyList;
template <class T> struct Length;
template <template <int...> class L,int...I>
struct Length<L<I...>> {
static const size_t value = sizeof...(I);
};
template <class T> constexpr size_t Length_v = Length<T>::value;
int main() {
using L1 = MyList<2,2>;
std::cout << Length_v<L1>;
}
However, the simpler alternative is to have a constexpr size_t length = sizeof...(I)
member in MyList
. The above is only necessary if MyList
itself does not have such member.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论