传递模板template参数而不指定具体类型

时间:2018-10-18 14:13:49

标签: c++ templates template-templates

我想围绕一个函数调用实现一个包装,该函数将执行以下操作:

template <template<class> class F>
void Wrapper(int n, F&& f)
{
    switch (n)
    {
        case 1:
            f<std::int8_t>();
            break;
        case 2:
            f<std::int16_t>();
            break;
        default:
            break;
    }
}

template <class T>
void func()
{
    // ... body of func()
}

这样我就可以在代码中进行以下调用:

Wrapper(1, func);

但是上述代码无法编译,因为F&& f构造无效-我需要指定参数的具体类型。但是,如果我使函数签名如下:

template <template<class> class F, class T>
void Wrapper(int n, F<T>&& f)

然后我必须使用f的具体类型进行呼叫:

Wrapper(1, func<std::int8_t>);

我将无法在Wrapper中进行切换。

如何实现所需的行为?

5 个答案:

答案 0 :(得分:6)

如果在编译时知道func(即它不是某个函数指针),则可以使用以下解决方案:

template <template <class> typename F>
void Wrapper(int n) {
  switch (n) {
    case 1: F<std::int8_t>{}(); break;
    case 2: F<std::int16_t>{}(); break;
    default: break;
  }
}

template <typename T>
void func() { std::cout << sizeof(T) << std::endl; }

template <typename T>
struct Func { void operator()() { func<T>(); } };

int main() {
  Wrapper<Func>(1);
  Wrapper<Func>(2);
}

答案 1 :(得分:2)

您可能会将代码重写为类似的内容

template <typename T> struct tag { using type = T;};

template <class F>
void Wrapper(int n, F&& f)
{
    switch (n)
    {
        case 1:
            f(tag<std::int8_t>());
            break;
        case 2:
            f(tag<std::int16_t>());
            break;
        default:
            break;
    }
}

template <class T>
void func()
{
    // ... body of func()
}

使用方式:

Wrapper(1, [](auto t){ return func<typename decltype(t)::type>()});

答案 2 :(得分:1)

C ++中有几种名称。

有价值观。有类型。有功能(和方法)。有成员。有类型模板。有功能模板。有可变模板。

这些不是标准中的正式名称。

在模板的情况下,实例化模板后,您会得到模板产生的东西。因此,实例化时的类型模板会给出类型,等等。

成员(函数和变量)可以通过成为成员函数/值的指针而成为值。成员变量名称可以通过与foo.x之类的对象配对而成为值。

通过重载解析,函数名称可以成为一个值。在某些情况下,函数名不会重载,因此重载解析是微不足道的。

等等。

函数参数始终是值。他们从来都不是模板。

模板参数可以是类型,类型模板或值。

您的代码正在尝试将模板作为值传递。不作弊就不能这样做。在您的特定情况下,您想传递模板函数。

函数模板是C ++中非常二等的公民。您不能将功能模板传递给任何其他模板,也不能将它们作为值传递。您可以对它们执行的操作非常受限制。

因此,我们需要将模板包装成 可以传递的内容。

这是一种方法:

template<class T>struct tag_t{using type=T;};
template<class T>constexpr tag_t<T> tag{};
template<class Tag>using type_t=typename Tag::type;

auto func_template = [](auto...tags) {
  return [](auto&&...args)->decltype(auto) {
    return func< type_t<decltype(tags)>... >( decltype(args)(args)... );
  };
};

现在func_template为几乎任意类型的template<class...Ts> R f(Args...)RArgs...包装了Ts...类型的模板函数

如果我们假设参数是用这种方式包装的,则可以这样做:

template <class F>
void Wrapper(int n, F&& f)
{
    switch (n)
    {
        case 1:
            f(tag<std::int8_t>)();
            break;
        case 2:
            f(tag<std::int16_t>)();
            break;
        default:
            break;
    }
}

然后在致电点进行:

Wrapper( 2, func_template );

它有效。

我们可以使用宏生成func_template

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  ->decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

#define FUNC_TEMPLATE(...) \
[](auto...tags) { \
  return [](auto&&...args) \
    RETURNS( __VA_ARGS__< type_t<decltype(tags)>... >( decltype(args)(args)... ) ); \
}

现在我们可以将呼叫站点更改为:

Wrapper( 2, FUNC_TEMPLATE(func) );

答案 3 :(得分:0)

您可以将int n移到模板参数吗?然后您可以使用Int2Type惯用语:

template<int n> struct ParamType {
    using value = ... // here will be std::int8_t or std::int16_t depending on n
}

template <template<class> class F, int n>
void Wrapper(F f) {
    f<ParamType<n>::value>();
}

答案 4 :(得分:0)

  

我将无法在Wrapper中进行切换。

     

如何实现所需的行为?

不幸的是,如果不修复模板类型,就无法将模板函数传递给另一个函数。

但是有很多方法可以使用模板类(或结构)来完成所需的工作。

例如,根据基于template-template参数的请求,您可以在模板结构中放置一个静态函数(我建议使用static,因此您不需要该类型的对象)

template <typename>
struct foo
 {
   static void func ()
    {
      // ... body of func()
    }
 };

,您必须通过foo并将其作为模板参数进行说明

Wrapper<foo>(1);
Wrapper<foo>(2);

Wrapper()成为

template <template <typename> class F>
void Wrapper (int n)
{
    switch (n)
    {
        case 1:
            F<std::int8_t>::func();
            break;
        case 2:
            F<std::int16_t>::func();
            break;
        default:
            break;
    }
}

另一种方法是使用模板方法创建非模板类/结构

struct bar
 {
   template <typename>
   static void func ()
    {
      // ... body of func()
    }
 };

您可以以相同的方式使用

Wrapper<bar>(1);
Wrapper<bar>(2);

或者,如果愿意,可以使用bar对象推断类型

bar  b0;

Wrapper(bar{}, 1);
Wrapper(b0,    2);

在第一种情况下,Wrapper的签名就是

template <typename F>
void Wrapper (int n)

在第二种情况下变为

template <typename F>
void Wrapper (F const &, int n)

在两种情况下,您都必须说明调用template的{​​{1}}

func()