2

次のようなテンプレート宣言があるとします。

template <class A, class B, class C = A (&)(B)>

タイプのオブジェクトを可変量持つことができるようにするにはどうすればよいCですか?可変class C ...c = x個引数テンプレートの引数にデフォルト値を設定できないため、これは機能しません。だからこれは私が試したものです:

template <typename T>
struct helper;

template <typename F, typename B>
struct helper<F(B)> {
    typedef F (&type)(B);
};

template <class F, class B, typename helper<F(B)>::type ... C>
void f(C ...c) { // error

}

しかし、最後の部分まで、エラーメッセージが表示されます。私はこれを正しくやっているとは思いません。私はここで何が間違っているのですか?

4

2 に答える 2

4

次のようなアプローチができると思います。まず、タイプ特性のためのいくつかの機械。これにより、引数パック内の型が同種であるかどうかを判断できます(すべての関数に同じシグネチャを持たせたいと思います)。

struct null_type { };

// Declare primary template
template<typename... Ts>
struct homogeneous_type;

// Base step
template<typename T>
struct homogeneous_type<T>
{
    using type = T;
    static const bool isHomogeneous = true;
};

// Induction step
template<typename T, typename... Ts>
struct homogeneous_type<T, Ts...>
{
    // The underlying type of the tail of the parameter pack
    using type_of_remaining_parameters = typename 
        homogeneous_type<Ts...>::type;

    // True if each parameter in the pack has the same type
    static const bool isHomogeneous = 
        is_same<T, type_of_remaining_parameters>::value;

    // If isHomogeneous is "false", the underlying type is a fictitious type
    using type = typename conditional<isHomogeneous, T, null_type>::type;
};

// Meta-function to determine if a parameter pack is homogeneous
template<typename... Ts>
struct is_homogeneous_pack
{
    static const bool value = homogeneous_type<Ts...>::isHomogeneous;
};

次に、ジェネリック関数のシグネチャを理解するためのいくつかの型特性:

template<typename T>
struct signature;

template<typename A, typename B>
struct signature<A (&)(B)>
{
    using ret_type = A;
    using arg_type = B;
};

そして最後に、これが可変個引数関数テンプレートを定義する方法です。

template <typename... F>
void foo(F&&... f)
{
    static_assert(is_homogeneous_pack<F...>::value, "Not homogeneous!");
    using fxn_type = typename homogeneous_type<F...>::type;

    // This was template parameter A in your original code
    using ret_type = typename signature<fxn_type>::ret_type;

    // This was template parameter B in your original code
    using arg_type = typename signature<fxn_type>::arg_type;

    // ...
}

簡単なテストは次のとおりです。

int fxn1(double) { }
int fxn2(double) { }
int fxn3(string) { }

int main()
{
    foo(fxn1, fxn2); // OK
    foo(fxn1, fxn2, fxn3); // ERROR! not homogeneous signatures
    return 0;
}

最後に、その引数パックを入手したらどうするかについてのインスピレーションが必要な場合は、私が書いた小さなライブラリをチェックしてください(この回答で使用されている機械のどの部分から取得されています)。引数パック内のすべての関数を呼び出す簡単な方法F... fは次のとおりです(@MarkGlisseへのクレジット):

initializer_list<int>{(f(forward<ArgType>(arg)), 0)...};

それをマクロで簡単にラップできます(私が投稿したリンクに対するMarkの回答を参照してください)。

完全でコンパイル可能なプログラムは次のとおりです。

#include <iostream>
#include <type_traits>

using namespace std;

struct null_type { };

// Declare primary template
template<typename... Ts>
struct homogeneous_type;

// Base step
template<typename T>
struct homogeneous_type<T>
{
    using type = T;
    static const bool isHomogeneous = true;
};

// Induction step
template<typename T, typename... Ts>
struct homogeneous_type<T, Ts...>
{
    // The underlying type of the tail of the parameter pack
    using type_of_remaining_parameters = typename
        homogeneous_type<Ts...>::type;

    // True if each parameter in the pack has the same type
    static const bool isHomogeneous =
        is_same<T, type_of_remaining_parameters>::value;

    // If isHomogeneous is "false", the underlying type is a fictitious type
    using type = typename conditional<isHomogeneous, T, null_type>::type;
};

// Meta-function to determine if a parameter pack is homogeneous
template<typename... Ts>
struct is_homogeneous_pack
{
    static const bool value = homogeneous_type<Ts...>::isHomogeneous;
};

template<typename T>
struct signature;

template<typename A, typename B>
struct signature<A (&)(B)>
{
    using ret_type = A;
    using arg_type = B;
};

template <typename F>
void foo(F&& f)
{
    cout << f(42) << endl;
}

template <typename... F>
void foo(typename homogeneous_type<F...>::type f, F&&... fs)
{
    static_assert(is_homogeneous_pack<F...>::value, "Not homogeneous!");
    using fxn_type = typename homogeneous_type<F...>::type;

    // This was template parameter A in your original code
    using ret_type = typename signature<fxn_type>::ret_type;

    // This was template parameter B in your original code
    using arg_type = typename signature<fxn_type>::arg_type;

    cout << f(42) << endl;
    foo(fs...);
}

int fxn1(double i) { return i + 1; }
int fxn2(double i) { return i * 2; }
int fxn3(double i) { return i / 2; }
int fxn4(string s) { return 0; }

int main()
{
    foo(fxn1, fxn2, fxn3); // OK

    // foo(fxn1, fxn2, fxn4); // ERROR! not homogeneous signatures

    return 0;
}
于 2013-01-11T03:38:33.730 に答える
3
template <typename T>
struct helper;

template <typename F, typename B>
struct helper<F(B)> {
    typedef F (*type)(B);
};

template<class F, class B>
void f()
{
}

template <class F, class B, typename... C>
void f(typename helper<F(B)>::type x, C... c)
{
    std::cout << x(B(10)) << '\n';
    f<F,B>(c...);
}

int identity(int i) { return i; }
int half(int i) { return i/2; }
int square(int i) { return i * i; }
int cube(int i) { return i * i * i; }

int main()
{
    f<int,int>(identity,half,square,cube);
}

タイプを推測できる修正バージョンは次のとおりです。

template<class F, class B>
void f(F(*x)(B))
{
    x(B());
}

template <class F, class B, typename... C>
void f(F(*x)(B), C... c)
{
    f(x);
    f<F,B>(c...);
}

int identity(int i) { return i; }
int half(int i) { return i/2; }
int square(int i) { return i * i; }
int cube(int i) { return i * i * i; }
int string_to_int(std::string) { return 42; }

int main()
{
    f(identity,half,square,cube);
    // f(identity,half,string_to_int);
}
于 2013-01-11T02:51:10.180 に答える