【问题标题】:Recursive metafunction using variadic templates使用可变参数模板的递归元函数
【发布时间】:2012-05-17 04:41:58
【问题描述】:

我正在编写一个元函数replace_type<C, X, Y>,它应该用Y 替换复合类型C 中所有X 类型的匹配项。我目前正在努力使其与C 中的可调用对象一起正常工作。

这行得通:

template replace_type<
    typename C, typename X, typename Y,
    typename First
>
struct replace_type<C(First), X, Y>
{
    typedef typename replace_type<
        C, X, Y
    >::type type(
        typename replace_type<
            First, X, Y
        >::type
    );
};

template replace_type<
    typename C, typename X, typename Y,
    typename First, typename Second
>
struct replace_type<C(First, Second), X, Y>
{
    typedef typename replace_type<
        C, X, Y
    >::type type(
        typename replace_type<
            First, X, Y
        >::type,
        typename replace_type<
            Second, X, Y
        >::type
    );
};

但这显然是非常有限的。在我的脑海中,我应该在这里使用可变参数模板似乎很明显,但是当我实际尝试应用它时,我很快注意到我不知道如何将它放入这个方案中。

我想过这样实现它:

template replace_type<
    typename C, typename X, typename Y,
    typename First, typename... Args
>
struct replace_type<C(First, Args...), X, Y>
{
    typedef typename replace_type<
        C, X, Y
    >::type type(
        typename replace_type<
            First, X, Y
        >::type,
        // How to recursively do the same with the rest of the arguments?
    );
};

这样,我总是可以访问第一个参数以适当地替换它,然后继续下一个,并准备好另一个专门的元函数来处理空函数作为我的递归的终止条件。问题是,如源代码中所述,如何在这种情况下开始递归?

更新

小例子:

#include <type_traits>

namespace type_replace_helper
{
    template <typename, typename, typename>
    struct type_replace_base;
}

template <typename C, typename X, typename Y>
struct type_replace
{
    typedef typename std::conditional<
        std::is_same<C, X>::value,
        Y,
        typename type_replace_helper::type_replace_base<
            C, X, Y
        >::type
    >::type type;
};

namespace type_replace_helper
{
    template <typename C, typename X, typename Y>
    struct type_replace_base
    {
        typedef C type;
    };

    template <typename C, typename X, typename Y>
    struct type_replace_base<C(), X, Y>
    {
        typedef typename type_replace<
            C, X, Y
        >::type type();
    };

    template <
        typename C, typename X, typename Y,
        typename First
    >
    struct type_replace_base<C(First), X, Y>
    {
        typedef typename type_replace<
            C, X, Y
        >::type type(
            typename type_replace<
                First, X, Y
            >::type
        );
    };

    template <
        typename C, typename X, typename Y,
        typename First, typename Second
    >
    struct type_replace_base<C(First, Second), X, Y>
    {
        typedef typename type_replace<
            C, X, Y
        >::type type(
            typename type_replace<
                First, X, Y
            >::type,
            typename type_replace<
                Second, X, Y
            >::type
        );
    };
}

int main()
{
    static_assert(std::is_same<
        type_replace<int(int, int), int, long>::type,
        long(long, long)
    >::value, "int should be replaced by long");
    return 0;
}

更新 2

多亏了 Crazy Eddie,我才得以实现我想要的。因为我花了很长时间才理解这个野兽般的答案,所以我认为阅读更详细的解决方案可能对其他人有所帮助。

我真正意识到的时间可能最长:问题不在于如何分离函数参数,而是将它们转换为替换参数的可变参数列表。因此,这里的主要目标是找到一种方法来正确替换每个参数,并将其存储到另一个单独的参数列表中。 Eddy 的解决方案使用stack 结构作为包装器来区分两个参数列表,一个已替换,一个有待处理。

一旦参数列表被一一替换并存储在stack结构中,剩下要做的就是再次将它们作为列表拉出并构造函数,如下所示:typedef T type(Params...);,就是这样.

在我的编码风格中,这显示为:

template <typename...>
struct stack {};

// Definition only to specialize for actual stacks
template <
    typename X, typename Y,
    typename Stack, typename... Todo
>
struct list_converter;

// No more arguments to convert, return the gathered stack
template <
    typename X, typename Y,
    typename... Elems
>
struct list_converter<X, Y, stack<Elems...>>
{
    typedef stack<Elems...> type;
};

// Push replaced argument to stack and go to the next argument
template <
    typename X, typename Y,
    typename... Elems,
    typename First, typename... Todo
>
struct list_converter<X, Y, stack<Elems...>, First, Todo...>
{
    typedef typename list_converter<
        X, Y,
        stack<
            typename replace_type<First, X, Y>::type,
            Elems...
        >,
        Todo...
    >::type type;
};

// Definition only again for stack specialization
template <
    typename C, typename X, typename Y,
    typename Stack
>
struct function_builder;

// Pull out argument list from the stack and build a function
template <
    typename C, typename X, typename Y,
    typename... Elems
>
struct function_builder<C, X, Y, stack<Elems...>>
{
    typedef typename replace_type<
        C, X, Y
    >::type type(Elems...);
};

// Specialization for function replacements
// Builds function with replaced return type, and converted
// argument list (recursion starts with empty stack)
template <
    typename C, typename X, typename Y,
    typename... Params
>
struct replace_type<C(Params...), X, Y>
{
    typedef typename function_builder<
        C, X, Y,
        typename list_converter<
            X, Y,
            stack<>,
            Params...
        >::type
    >::type type;
};

以上代码如有语法错误请见谅;它已经是一个相当大的文件,我试图只提取相关信息。

【问题讨论】:

  • 我不明白你在做什么。您的第一个示例甚至无法编译。
  • 这是我的专长的一部分,而不是一个最小的例子。给我一点时间,我给你加一个。
  • @fontanini 我添加了一个带有简单测试用例的示例,该示例应该演示我想要完成的工作。

标签: c++ c++11 metaprogramming variadic-templates


【解决方案1】:

可变参数包的扩展可以处理所谓的模式。因此,您可以对所有事情使用一种偏特化。

template replace_type<
    typename R, typename... Args,
    typename X, typename Y
>
struct replace_type<R(Args...), X, Y>
{
    typedef typename replace_type<
        R, X, Y
    >::type type(
        typename replace_type<
            Args, X, Y
        >::type...
    );
};

【讨论】:

    【解决方案2】:
    template < typename ... A >
    struct stack { };
    
    template < typename Stack, typename T >
    struct push_front;
    
    template < typename T, typename ... A >
    struct push_front<stack<A...>,T> {
        typedef stack<T, A ... > type;
    };
    
    template < typename Ret, typename Args >
    struct build_fun;
    
    template < typename Ret, typename ... A >
    struct build_fun<Ret, stack<A...> > {
        typedef Ret(*fptr)(A...);
        typedef decltype(*static_cast<fptr>(0)) type;
    };
    
    template < typename Match, typename Rep, typename Target >
    struct replace_match { typedef Target type; };
    
    template < typename Match, typename Rep >
    struct replace_match<Match, Rep, Match> { typedef Rep type; };
    
    template < typename Match, typename Rep, typename ... Types >
    struct replace;
    
    template < typename Match, typename Rep, typename Head, typename ... Tail >
    struct replace<Match,Rep,Head,Tail...>
    {
        typedef typename replace_match<Match,Rep,Head>::type my_match;
    
        typedef typename replace<Match, Rep, Tail...>::type next_set;
    
        typedef typename push_front<next_set, my_match>::type type;
    };
    
    template < typename Match, typename Rep >
    struct replace<Match,Rep>
    {
        typedef stack<> type;
    };
    
    template < typename Sig, typename Match, typename Rep>
    struct replace_fun_args;
    
    template < typename R, typename Match, typename Rep, typename ... Args >
    struct replace_fun_args
    {
        typedef typename replace<Match, Rep, Args...>::type arg_stack;
        typedef typename build_fun<R,arg_stack>::type type;
    };
    
    #include <iostream>
    #include <typeinfo>
    
    int main() {
    
        replace<int,char,double,unsigned int, int, char*>::type t;
    
        std::cout << typeid(build_fun<void,decltype(t)>::type).name() << std::endl;
    }
    

    可能有一种方法可以只使用包而不是 stack 模板...需要查找如何从类型构建包。

    【讨论】:

    • “感谢您帮我解决这个问题,Eddie!” :P
    • 感谢您帮我解决这个问题,埃迪!很抱歉放低了,但我一直在玩弄你在那里所做的事情。老实说,我还没有掌握你所做的一切,尤其是函数指针在该上下文中所做的事情,但我会在某个时候管理它(希望如此)
    • 好的,我想我明白了。 build_fun&lt;T, stack&lt;Params...&gt;&gt;::type 映射到 T (&amp;)(Params...) - 这很好!谢谢
    • 不幸的是,除了首先创建函数指针类型之外,我找不到另一种创建函数类型的方法。我尝试过的各种语法都不起作用。抱歉回复晚了 - 正在用 C 语言编写 boost.bind :P
    • 我已经完成了以我的编码风格制定的方法版本。我还更新了我的帖子,对这个解决方案的工作原理做了一些详细的解释。我希望我仍然能很好地代表你想法的精髓。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-23
    • 1970-01-01
    • 1970-01-01
    • 2018-08-19
    • 1970-01-01
    • 2013-08-04
    • 2016-12-11
    相关资源
    最近更新 更多