如何使用模板元编程压缩自定义结构

huangapple go评论70阅读模式
英文:

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&lt;int ...&gt;
class MyList;

template&lt;int H, int ... T&gt;
class MyList&lt;H, T ...&gt; {
public:
    static const int Head = H;
    using Tail = MyList&lt;T ...&gt;;
};

template&lt;&gt;
class MyList&lt;&gt; {};

//list lenght
template&lt;typename TL&gt;
class Length {
public:
    static const int value = 1 + Length&lt;typename TL::Tail&gt;::value;
};

template&lt;&gt;
class Length&lt;MyList&lt;&gt;&gt; {
public:
    static const int value = 0;
};

//Add an element to front
template&lt;int H, typename IL&gt;
class IntCons;

template&lt;int H, int... Tail&gt;
class IntCons&lt;H, MyList&lt;Tail...&gt;&gt;{
public:
    using type = MyList&lt;H, Tail...&gt;;
};

//Generate list with N elements
template &lt;int N, int ... Next&gt;
class Generate : public Generate&lt;N-1, N-1, Next...&gt; { };

template &lt;int ... Next&gt;
struct Generate&lt;0, Next ... &gt; {
    using type = MyList&lt;Next ... &gt;;
};

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&lt;int x, int y&gt;
struct Multiply {
    static int const value = x * y;
};

Calculate metafunction

template&lt;typename L1, typename L2, template&lt;int, int&gt; class MetaF, class... Tail&gt;
struct Calculate;

template&lt;typename L1, typename L2, template&lt;int, int&gt; class MetaF, class... Tail&gt;
struct Calculate : Calculate&lt;typename L1::Tail, typename L2::Tail, MetaF, Tail...&gt; {
static_assert( Length&lt;L1&gt;::value == Length&lt;L2&gt;::value,
    &quot;The arguments of lists should be of the same size&quot; );

    using type = IntCons&lt;MetaF&lt;L1::Head, L2::Head&gt;::value, Tail...&gt;;
};

If L1 = MyList&lt;2,2,3,3,5&gt; and L2 = MyList&lt;5,3,8,7,3&gt; I am waiting result MyList&lt;10, 6, 24, 21, 15&gt; 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&lt;2,2&gt;), then plug it back in, and for convenience a _t alias:

#include &lt;iostream&gt;
#include &lt;type_traits&gt;

template&lt;int ...&gt; class MyList;


template&lt;int x, int y&gt; struct Multiply {
    static int const value = x * y;
};

template&lt;typename L1, typename L2, template&lt;int, int&gt; class MetaF&gt;
struct Calculate;

template &lt;template &lt;int,int&gt; class MetaF,template&lt;int...&gt; class ML,int... I1,int... I2&gt;
struct Calculate&lt; ML&lt;I1...&gt;,ML&lt;I2...&gt;,MetaF&gt; {
    using type = ML&lt; MetaF&lt;I1,I2&gt;::value ...&gt;;
};

template &lt;typename L1,typename L2, template&lt;int,int&gt; class MetaF&gt;
using Calculate_t = typename Calculate&lt;L1,L2,MetaF&gt;::type;

int main() {
    using L1 = MyList&lt;2,2&gt;;
    using L2 = MyList&lt;5,3&gt;;
    using R = MyList&lt;10,6&gt;;
    std::cout &lt;&lt; std::is_same_v&lt;R, Calculate_t&lt;L1,L2,Multiply&gt;&gt;;
}

Output:

1

PS: If you do need to infer the length of the list, use partial specialization and the sizeof... operator:

#include &lt;iostream&gt;
#include &lt;type_traits&gt;

template&lt;int ...&gt; class MyList;

template &lt;class T&gt; struct Length;
template &lt;template &lt;int...&gt; class L,int...I&gt;
struct Length&lt;L&lt;I...&gt;&gt; {
    static const size_t value = sizeof...(I);
};

template &lt;class T&gt; constexpr size_t Length_v = Length&lt;T&gt;::value;

int main() {
    using L1 = MyList&lt;2,2&gt;;
    std::cout &lt;&lt; Length_v&lt;L1&gt;;
}

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.

huangapple
  • 本文由 发表于 2023年7月18日 03:00:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76707388.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定