【问题标题】:How do I make this template argument variadic?如何制作这个模板参数变量?
【发布时间】:2012-12-25 14:28:27
【问题描述】:

假设我有一个这样的模板声明:

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

}

但直到最后一部分,我都会收到错误消息。我不认为我这样做是正确的。我在这里做错了什么?

【问题讨论】:

  • 如果你只使用template &lt;class A, class B, class C = A (&amp;)(B), class D...&gt;呢?
  • 采取任何措施,并使用enable_if 和特征类进行验证?
  • 确实采取任何措施似乎是更好的方法

标签: c++


【解决方案1】:

我认为您可以使用以下方法。首先,一些类型特征的机制。这使您可以确定参数包中的类型是否是同质的(我想您希望所有函数都具有相同的签名):

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

最后,如果您在获得该参数包后需要做什么的灵感,您可以查看a small library I wrote(取自本答案中使用的机器的哪一部分)。调用参数包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;
}

【讨论】:

  • 这确实很好,但并不是我希望所有方法都是同质的。它们可能不同
  • @templateboy:谢谢,但我可能遗漏了一些东西。他们怎么可能不同?即使在您接受的解决方案中也不能,并且使用该解决方案您必须明确提供签名
  • 是的,我也被那个评论弄糊涂了。我认为这就是 “我将如何制作它以便我可以拥有可变数量的 C 类型对象” 的意思
  • 好吧,不要介意最后的评论。你说得对,我确实这么说。
【解决方案2】:
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);
}

【讨论】:

  • 这仍然不是我要问的。我想要一个可以在函数内部调用的函数指针的可变参数列表。这不会那样做。
  • @BenjaminLindley:但是这种方法会强制您明确指定模板参数,对吗?它无法推断它们
  • @BenjaminLindley:所以由于所有函数最终都必须具有相同的签名,并且我的解决方案可以从参数中推断出来,我会说我的方法更好......我错了吗?
  • @AndyProwl:也许吧。不幸的是,我无法编译您的代码。您介意提供一个完整的连续可编译列表吗?
  • @BenjaminLindley:当然,我会这样做并将其添加到我的答案中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-20
  • 2016-09-10
  • 1970-01-01
  • 1970-01-01
  • 2021-04-04
相关资源
最近更新 更多