作为模板参数传递的函数指针
Function pointers passed as template arguments
我正在阅读一些代码,我发现了一个函数指针(地址,而不是类型)被传递给模板参数的情况。
// connects a free function
registry.on_construct<position>().connect<&my_free_function>();
// connects a member function
registry.on_construct<position>().connect<&my_class::member>(instance);
我以前没见过这个。因为我认为只有类型和整数可以传递给模板参数,我认为你需要做这样的事情:
connect<decltype(&my_free_function)>(&my_free_function);
我用了好久才意识到这可能是一种静态绑定和调用函数指针的方式?我以前从未见过。被调用的代码看起来像这样:
void my_free_function() { std::cout << "free function called\n"; }
template <auto Function>
void templatedFunc()
{
std::cout << typeid(Function).name();
// The type of Function according to type_info is void (__cdelc*)(void)
Function(); // This means we call Function function pointer with no dynamic/runtime binding?
}
int main()
{
templatedFunc<&my_free_function>();
}
这是在做我想做的事情吗,即函数指针的静态绑定调用?模板参数是否需要自动才能工作? 'typename'、'class' 和 'int' 都不起作用。
Is this doing what I think it's doing, ie., statically bound call of a function pointer?
是的,这正是您认为的那样。它将参数绑定为类型本身的一部分,因此不需要将其作为运行时参数传递
Does the template parameter need to be auto for this to work?
不——auto
只是让它更灵活,因此它可以接受任何可调用对象的实例——无论它是一个函数指针,一个通用的lambda ,甚至是仿函数对象。
对于函数指针,也可以这样写:
template <typename R, typename...Args>
class signal<R(Args...)>
{
...
template <R(*Fn)(Args...)>
auto connect() -> void;
...
};
但是请注意,在执行此操作时它会强制使用 exact 签名——这不是那么有表现力。例如,你不能这样做:
float add(float, float);
auto sig = signal<double(double,double)>{};
sig.connect<&add>(); // error: float(*)(float, float) cannot be converted to double(*)(double, double) for template param
使用 auto
,它允许您 推导 参数类型——并且可以允许像这样的模板支持隐式转换——这实际上可以是如果使用得当,它非常强大。这有点像 std::function
可以用相似但不相同的参数绑定一个函数并让它仍然有效——除了这一切都是静态完成的而不是动态完成的。
如果您好奇为什么此模式用于运行时参数,这是因为它启用了一种非常简单的类型擦除 在构建回调系统时。保持函数静态允许编译器比 std::function
更好地内联代码,并且它的内存占用也更小。
通过使用函数 template
s,实例化 函数模板的签名永远不会改变——这允许以同质的方式存储它以备后用:
template <typename R, typenmae...Args>
class delegate<R(Args...)> {
...
// The function that will expand into the same pointer-type each time
template <auto Fn>
static auto invoke_stub(Args...) -> R;
...
// The stub to store
R(*m_stub)(Args...);
...
};
对于 delegate<int(int)>
,invoke_stub
是否使用类型为 short(*)(short)
、int(*)((short)
等的函数指针进行扩展并不重要——decltype(invoke_stub<...>)
仍将始终产生 R(*)(Args...)
—— 这使得在内部存储它变得非常容易,以便稍后可以调用它。
(注意:这个描述有点简化——通常存根包含稍微更多的信息来支持成员函数)
要更深入地了解如何做到这一点,您可以查看我几个月前撰写的一些教程,这些教程恰好与这个主题有关:Creating a fast and efficient delegate. Part 2 discusses auto
parameters, and Part 3 将执行时间与原始函数进行比较指针。
我正在阅读一些代码,我发现了一个函数指针(地址,而不是类型)被传递给模板参数的情况。
// connects a free function
registry.on_construct<position>().connect<&my_free_function>();
// connects a member function
registry.on_construct<position>().connect<&my_class::member>(instance);
我以前没见过这个。因为我认为只有类型和整数可以传递给模板参数,我认为你需要做这样的事情:
connect<decltype(&my_free_function)>(&my_free_function);
我用了好久才意识到这可能是一种静态绑定和调用函数指针的方式?我以前从未见过。被调用的代码看起来像这样:
void my_free_function() { std::cout << "free function called\n"; }
template <auto Function>
void templatedFunc()
{
std::cout << typeid(Function).name();
// The type of Function according to type_info is void (__cdelc*)(void)
Function(); // This means we call Function function pointer with no dynamic/runtime binding?
}
int main()
{
templatedFunc<&my_free_function>();
}
这是在做我想做的事情吗,即函数指针的静态绑定调用?模板参数是否需要自动才能工作? 'typename'、'class' 和 'int' 都不起作用。
Is this doing what I think it's doing, ie., statically bound call of a function pointer?
是的,这正是您认为的那样。它将参数绑定为类型本身的一部分,因此不需要将其作为运行时参数传递
Does the template parameter need to be auto for this to work?
不——auto
只是让它更灵活,因此它可以接受任何可调用对象的实例——无论它是一个函数指针,一个通用的lambda ,甚至是仿函数对象。
对于函数指针,也可以这样写:
template <typename R, typename...Args>
class signal<R(Args...)>
{
...
template <R(*Fn)(Args...)>
auto connect() -> void;
...
};
但是请注意,在执行此操作时它会强制使用 exact 签名——这不是那么有表现力。例如,你不能这样做:
float add(float, float);
auto sig = signal<double(double,double)>{};
sig.connect<&add>(); // error: float(*)(float, float) cannot be converted to double(*)(double, double) for template param
使用 auto
,它允许您 推导 参数类型——并且可以允许像这样的模板支持隐式转换——这实际上可以是如果使用得当,它非常强大。这有点像 std::function
可以用相似但不相同的参数绑定一个函数并让它仍然有效——除了这一切都是静态完成的而不是动态完成的。
如果您好奇为什么此模式用于运行时参数,这是因为它启用了一种非常简单的类型擦除 在构建回调系统时。保持函数静态允许编译器比 std::function
更好地内联代码,并且它的内存占用也更小。
通过使用函数 template
s,实例化 函数模板的签名永远不会改变——这允许以同质的方式存储它以备后用:
template <typename R, typenmae...Args>
class delegate<R(Args...)> {
...
// The function that will expand into the same pointer-type each time
template <auto Fn>
static auto invoke_stub(Args...) -> R;
...
// The stub to store
R(*m_stub)(Args...);
...
};
对于 delegate<int(int)>
,invoke_stub
是否使用类型为 short(*)(short)
、int(*)((short)
等的函数指针进行扩展并不重要——decltype(invoke_stub<...>)
仍将始终产生 R(*)(Args...)
—— 这使得在内部存储它变得非常容易,以便稍后可以调用它。
(注意:这个描述有点简化——通常存根包含稍微更多的信息来支持成员函数)
要更深入地了解如何做到这一点,您可以查看我几个月前撰写的一些教程,这些教程恰好与这个主题有关:Creating a fast and efficient delegate. Part 2 discusses auto
parameters, and Part 3 将执行时间与原始函数进行比较指针。