参数列表中间的变量模板参数

时间:2018-06-09 02:20:53

标签: c++ c++11 templates variadic-templates template-meta-programming

我正在尝试指定一个将泛型函数作为参数的函数。该函数定义如下:

template <typename TRet, typename... TsArgs>
using Fun = TRet (*)(TsArgs...);

如何指定将该泛型函数作为参数的泛型函数?我试过这个:

template<typename TRet, typename... TsArgs, Fun<TRet, TsArgs...> F>
TRet wrap(TsArgs... args) {
  return F(args...);
}

包装此功能:

bool foo(int x, double y) {
    return x < y;
}

像这样:

Fun<bool, int, double> func = wrap<bool, int, double, foo>;

然而,不幸的是,这不会编译。 gcc 8.1有以下错误消息:

<source>:16:35: error: no matches converting function 'wrap' to type 'Fun<bool, int, double>' {aka 'bool (*)(int, double)'}
     Fun<bool, int, double> func = wrap<bool, int, double, foo>;
                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

和clang 6出现以下错误:

<source>:16:35: error: address of overloaded function 'wrap' does not match required type 'bool (int, double)'
    Fun<bool, int, double> func = wrap<bool, int, double, foo>;
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

但是,如果我将TsArgs替换为int, double,就像foo()的签名一样,它编译得很好,让我相信在中间的那些可变参数模板参数参数列表不能像我预期的那样工作。我怎么能实现我的目标呢?

这是MCVE:

template <typename TRet, typename... TsArgs>
using Fun = TRet (*)(TsArgs...);

template<typename TRet, typename... TsArgs, Fun<TRet, TsArgs...> F>
TRet wrap(TsArgs... args) {
    return F(args...);
}

bool foo(int x, double y) {
    return x < y;
}

int main() {
    Fun<bool, int, double> func = wrap<bool, int, double, foo>;
    return 0;
}

2 个答案:

答案 0 :(得分:2)

也许您可以分两步制作wrap :( 1)接收TRetTArgs...模板参数的模板结构和(2)模板静态func()功能接收Fun<TRet, TsArgs...>模板参数。

以下内容

template <typename TRet, typename... TsArgs>
struct wrap
 {
   template <Fun<TRet, TsArgs...> F>
   static TRet func (TsArgs ... args)
    { return F(args...); }
 };

你可以用这种方式

Fun<bool, int, double> func = wrap<bool, int, double>::func<foo>;

以下是一个完整的工作示例

#include <iostream>

template <typename TRet, typename... TsArgs>
using Fun = TRet (*)(TsArgs...);

template <typename TRet, typename... TsArgs>
struct wrap
 {
   template <Fun<TRet, TsArgs...> F>
   static TRet func (TsArgs ... args)
    { return F(args...); }
 };

bool foo(int x, double y)
 { return x < y; }

int main()
 {
   Fun<bool, int, double> func = wrap<bool, int, double>::func<foo>;

   std::cout << func(1, 2.0) << std::endl;
 }

答案 1 :(得分:2)

如果您可以使用C ++ 17,则可以使用auto作为模板参数。

这可以简化我之前的答案。

或更好:简化包装器的使用。

如果您定义wrapHelper结构如下

template <typename T, T>
struct wrapHelper;

template <typename TRet, typename... TsArgs, Fun<TRet, TsArgs...> F>
struct wrapHelper<Fun<TRet, TsArgs...>, F>
 {
   static TRet func (TsArgs ... args)
    { return F(args...); }
 };

您可以使用wrapper撰写auto,如下所示

template <auto X>
struct wrap : public wrapHelper<decltype(X), X>
 { };

这样就不需要解释返回类型(TRet)和参数类型(TsArgs...):它们是从wrapHelper中推导出来的foo}

所以,而不是

Fun<bool, int, double> func = wrap<bool, int, double>::func<foo>;

你必须写

Fun<bool, int, double> func = wrap<foo>::func;

auto func = wrap<foo>::func;

以下是完整的编译示例

#include <iostream>
#include <type_traits>

template <typename TRet, typename... TsArgs>
using Fun = TRet (*)(TsArgs...);

bool foo(int x, double y)
 { return x < y; }

template <typename T, T>
struct wrapHelper;

template <typename TRet, typename... TsArgs, Fun<TRet, TsArgs...> F>
struct wrapHelper<Fun<TRet, TsArgs...>, F>
 {
   static TRet func (TsArgs ... args)
    { return F(args...); }
 };

template <auto X>
struct wrap : public wrapHelper<decltype(X), X>
 { };

int main()
 {   
   auto func { wrap<foo>::func };

   static_assert( std::is_same<decltype(func), Fun<bool, int, double>>{} );

   std::cout << func(1, 2.0) << std::endl;
 }