c++ - 参数列表中间的可变模板参数

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

最佳答案

如果你可以使用 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;
 }

关于c++ - 参数列表中间的可变模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50770350/

相关文章:

c++ - 什么是 "Argument-Dependent Lookup"(又名 ADL,或 "Koenig Lookup")?

c++ - 在模板函数上设置默认参数(函数类型)

c++ - 编译器对带有元组参数的函数的混淆

c++ - 全局函数和不明确的参数 NULL 与 char* 在 vs 2013 和 GCC 之间

c++ - 最大数 < x?

c++ - 我应该何时/为什么(如果有的话)考虑进行泛型编程/元编程

c++ - 通过将模板作为参数传递来简化模板函数?

c++ - 线路抑制状态错误 LNK2005 _main 已在 lua.obj 中定义

c++ - 睡了很多天,分辨率为微秒

c++ - 将函数调用保存在容器中