我想围绕一个函数调用实现一个包装,该函数将执行以下操作:
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
中进行切换。
如何实现所需的行为?
答案 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...)
,R
和Args...
包装了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()