简体   繁体   English

如何使此模板参数可变参数?

[英]How do I make this template argument variadic?

Say I have a template declaration like this: 说我有一个这样的模板声明:

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

How would I make it so that I could have a variable amount of objects of type C ? 我将如何做到这一点,以使我拥有数量可变的C类型对象? Doing class C ...c = x won't work because variadic template arguments can't have default values. 执行class C ...c = x将不起作用,因为可变参数模板参数不能具有默认值。 So this is what I've tried: 所以这就是我尝试过的:

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

}

But up to the last part I get error messages. 但是到最后一部分,我仍然收到错误消息。 I don't think I'm doing this right. 我认为我做的不对。 What am I doing wrong here? 我在这做错了什么?

I think you can use the following approach. 我想你可以使用以下方法。 First, some machinery for type traits. 首先,一些用于类型特征的机制。 This allows you to determine if the types in an argument pack are homogeneous (I guess you want all functions to have the same signature): 这允许您确定参数包中的类型是否是同类的(我想您希望所有函数具有相同的签名):

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

Then, some more type traits to figure out the signature of a generic function: 然后,更多类型特征来计算泛型函数的签名:

template<typename T>
struct signature;

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

And finally, this is how you would define your variadic function template: 最后,这是您定义可变参数函数模板的方法:

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;

    // ...
}

Here is a short test: 这是一个简短的测试:

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

Finally, if you need an inspiration on what to do once you have that argument pack, you can check out a small library I wrote (from which part of the machinery used in this answer is taken). 最后,如果你需要一个灵感,一旦你有了这个参数包,该怎么做,你可以看看我写的一个小型图书馆 (从这个答案中使用的部分机器)。 An easy way to call all the functions in the argument pack F... f is the following (credits to @MarkGlisse): 调用参数包F... f中的所有函数的简单方法如下(@MarkGlisse的信用):

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

You can easily wrap that in a macro (just see Mark's answer to the link I posted). 您可以轻松地将其包装在宏中(请参阅Mark对我发布的链接的回答)。

Here is a complete, compilable program: 这是一个完整的可编译程序:

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

Here's a modified version that can deduce types: 这是一个可以推断出类型的修改版本:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用可变参数 arguments 使这个模板专业化成为 class 的朋友? - How do I make this template specialization with variadic arguments a friend of a class? 如何访问可变参数模板参数包的成员中存在的内部模板typedef? - How do I access an inner template typedef present in the members of a variadic template argument pack? 如何使用函数指针作为非类型参数制作可变参数模板函数? - how can I make variadic template function with a function pointer as non type argument? 如何在0可变参数上专门化可变参数模板类? - How do I specialise a variadic template class on 0 variadic arguments? 如何在可变参数模板类中获取函数指针的参数类型? - How do I get the argument types of a function pointer in a variadic template class? 如何在条件下展开可变参数模板? - How Do I Unroll a Variadic Template in a Condition? 如何将可变参数std :: tuple用作模板参数? - How can I take a variadic std::tuple as a template argument? 如何遍历打包的可变参数模板参数列表? - How can I iterate over a packed variadic template argument list? 如何反省可变参数模板模板参数的arity? - How to introspect the arity of a variadic template template argument? 如何使用变量模板类方法将函数指针作为参数与从函数模板派生的类型? - How to make variadic template class method take function pointer as argument with type derived from function template?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM