3

複合型replace_type<C, X, Y>の型のすべての一致を に置き換えるメタ関数を作成しています。私は現在、これを の callable で適切に動作させることに取り組んでいます。XCYC

これは機能します:

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?
    );
};

このようにして、常に最初のパラメーターにアクセスして適切に置き換えてから、次のパラメーターに進み、nullary 関数を再帰の終了条件として処理するための別の特殊なメタ関数を用意することができます。質問は、ソース コードに記載されているように、このコンテキストで再帰を開始するにはどうすればよいですか?

アップデート

最小限の例:

#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構造体をラッパーとして使用して、2 つのパラメーター リストを区別しています。

パラメータ リストを 1 つずつ置き換えてstack構造体に格納したら、あとはそれらをリストとして再度取り出して、 so: のように関数を構築する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;
};

上記のコードに構文エラーがある場合はご容赦ください。それはすでにかなり大きなファイルであり、関連する情報のみを抽出しようとしました。

4

2 に答える 2

3

可変個パックの展開は、いわゆるパターンを扱うことができます。したがって、すべてに対して 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...
    );
};
于 2012-05-10T06:52:04.660 に答える
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ます...タイプからパックを構築する方法を調べる必要があります。

于 2012-05-08T17:26:39.093 に答える