如何编写一个将函数用作模板参数的模板,并通过该函数的参数类型自动推导出其他类型名?
void foo(int *p) {}
template<typename T, void (*F)(T*)>
struct bar
{
bar(T* t)
{
F(t);
}
}
int *p;
bar<int, foo> b(p); // both int and foo are required here
如何编写仅支持使用foo
作为参数的模板
bar<foo> b(p);
答案 0 :(得分:3)
如果可以将c ++ 17与它的自动模板参数一起使用(如注释中的@ n.m。),则可以将其用作模板参数,然后将其用作具有特征类型的T类型。
首先,我们需要标准类型特征和类型特征,以获取我们可以这样编写的一元函数(您的foo)的参数:
#include <type_traits>
// Trait to get the argument type of a unary function
template<typename T>
struct unary_func_arg;
template<typename R, typename T>
struct unary_func_arg< R(*)(T) >
{
using type = T;
};
如果由于未声明主专业化而将函数指针以外的任何内容放入其中,则会产生错误。
之后,我们终于可以这样写了吧:
template< auto F >
struct bar
{
// Assert it's a function pointer
static_assert( std::is_pointer_v<decltype(F)> );
static_assert( std::is_function_v< std::remove_pointer_t<decltype(F)> > );
// Get the parameter type
using T = typename unary_func_arg< decltype(F) >::type;
bar(T t)
{
F(t);
}
};
我们必须确保F是一个函数指针,所以我们必须静态声明它,然后从类型特征中获取类型T。
现在您可以像这样声明f和b:
int* p;
bar<foo> b(p);
编辑:如果您需要T不是指针,那么您可以编写T *,则可以使类型特征删除1个指针级别,也可以在此处将类型特征修改为:
// Trait to get the argument type of a unary function
template<typename T>
struct unary_func_arg_pointer;
template<typename R, typename T>
struct unary_func_arg_pointer< R(*)(T*) >
{
using type = T;
};
在此示例中,现在T将只是int
答案 1 :(得分:1)
在C ++ 11中,类无法推断出所传递函数的所有类型。但是功能可以。 因此可以编写此函数:
template<typename Ret, typename Param>
Deduced_Types<Ret, Param> deduce_type(Ret (*)(Param))
{
return Deduced_Types<Ret, Param>();
}
此函数使用一种类型来存储推导的类型(必须在该函数之前声明):
template<typename Ret, typename Param>
class Deduced_Types
{
public:
typedef Ret Return_type;
typedef Param Parameter_type;
};
现在要对其进行测试;
int f(bool)
{
return 0;
}
int main(int argc, char* argv[])
{
decltype( deduce_type(f) )::Return_type i = 0;
return i;
}
有效。
现在开始吧:
template< class F >
class Bar
{
public:
typedef typename F::Return_type Func_Return_type;
typedef typename F::Parameter_type Fun_Param_type;
};
必须被称为:
Bar< decltype(deduce_type(f)) > b;
(您可以在此处使用宏)
在gcc 4.8.1上工作:https://godbolt.org/z/1cz2pk
编辑:
不能将C ++ 17之前的函数传递到模板中。
所以需要的是您需要将函数指针传递给类。还有一个static_assert
来验证参数是否正确:
#include <type_traits>
struct bar
{
template<typename F, typename T>
bar(F f, T t)
{
typedef decltype(deduce_type(f)) D;
static_assert(std::is_same<typename D::Parameter_type, T>::value,"parameter has different type function parameter");
f(t);
}
};
现在,函数指针作为参数传递,而不是将函数作为模板参数传递:
void foo(int *p) {}
int *p;
bar b(foo, p);
这里唯一的问题是该类必须存储此指针以备将来使用。
答案 2 :(得分:1)
在C ++ 17中,您可以这样做
template <auto> struct bar;
template<typename T, void (*F)(T*)>
struct bar<F>
{
bar(T* t) { F(t); }
};
用法:
int* p = nullptr;
bar<foo> b(p);
在C ++ 17之前,您可以这样做:
template <typename T, T> struct bar;
template<typename T, void (*F)(T*)>
struct bar<void (*)(T*), F>
{
bar(T* t) { F(t); }
};
使用方式:
int* p = nullptr;
bar<decltype(&foo), &foo> b(p);
MACRO可用于删除重复项,例如:
#define BAR(Func) bar<decltype(&Func), &Func>
#define TYPE_AND_VALUE(V) decltype(V), V>
允许:
BAR(foo) b(p);
bar<TYPE_AND_VALUE(&foo)> b(p);