如何将函数传递给模板函数 A,该函数可能具有基于 A 的另一个参数的不同签名?
How can I pass a function to a templated function A which may have a different signature based on another parameter to A?
我想创建一个使用类型的模板,并传递一个函数,该函数的模板参数包含该类型。
这是我目前拥有的:
#include <iostream>
void fooRef(int& ref) { std::cout << "In ref" << std::endl; }
void fooPtr(int* ptr) { std::cout << "In ptr" << std::endl; }
template<typename T, typename F>
void Print(T arg, F func) {
//DoABunchOfStuff();
func(arg);
//DoSomeMoreStuff();
}
int main() {
int x = 5;
int& ref = x;
Print<int*, void(*)(int*)>(&x, &fooPtr);
Print<int&, void(*)(int&)>(ref, &fooRef);
}
这行得通,但我觉得函数的调用者可能会有些冗长。理想情况下,我希望调用看起来像:
Print<int*, fooPtr>(ptr);
Print<int&, fooRef>(ref);
有没有办法简化打印函数的调用?
Is there a way to simplify the calls to the Print function?
是的。您所做的根本不是指定模板类型。函数模板经过一个过程调用 template argument deduction。在此过程中,传递给函数的参数会推导出它们的类型,编译器会尝试将其与模板参数相匹配。如果有效,则该函数将被清除,编译器将继续。所以如果我们使用
int main() {
int x = 5;
int& ref = x;
Print(&x, &fooPtr);
Print(std::ref(ref), &fooRef);
}
然后我们得到
In ptr
In ref
在第二次调用中,我使用了 std::ref
,因此它实际上会通过引用传递 ref
。否则它会复制 ref
所指的内容。
您可以使用模板参数推导和可变参数模板来简化它。方法如下:
template<typename FunctionType, typename... ArgumentTypes>
void Print(FunctionType function, ArgumentTypes... args) {
function(args...);
}
但是,如果您想正确支持引用,则必须求助于转发引用,这让编译器不仅可以推断出所发送参数的类型,而且也是值类型。
template<typename F, typename... Args>
void Print(F function, Args&&... args) {
function(std::forward<Args>(args)...);
}
现在一切似乎都很好,但您仍然需要处理接受任何参数的函数。限制您的函数只接受将使调用有效的参数可以使用尾随 return 类型来完成:
// instead of specifying the return type before the function name,
// let's place it at the end and use `decltype` to deduce the right type:
template<typename F, typename... Args>
auto Print(F function, Args&&... args) -> decltype(function(std::declval<Args>()...)) {
return function(std::forward<Args>(args)...);
}
使用此代码,您的编译器会出现更清晰的错误:
void func1(int) {}
Print(func1, 1); // works!
Print(func1, 1, 7); // error: no such function to call
如果您仍然希望 void
作为您的 return 类型并且仍然限制您的功能,您可以依靠 void_t
来做到这一点:
// void_t definition
template<typename...>
using void_t = void;
template<typename F, typename... Args>
auto Print(F function, Args&&... args) -> void_t<decltype(function(std::declval<Args>()...))> {
function(std::forward<Args>(args)...);
}
是的,您理想的语法很简单:
void fooRef(int& ref) { (void)ref; std::cout << "In ref" << std::endl; }
void fooPtr(int* ptr) { (void)ptr; std::cout << "In ptr" << std::endl; }
template<class T, void(*func)(T)>
void Print(T arg) {
//DoABunchOfStuff();
func(std::forward<Arg>(arg));
//DoSomeMoreStuff();
}
int main() {
int x = 5;
int& ref = x;
Print<int*, fooPtr>(&x);
Print<int&, fooRef>(ref);
}
添加 (void)ref
和 (void)ptr
以抑制未使用的变量警告。 std::forward<T>
用于提高一些极端情况的效率。我还建议在 T
上阻止扣除:
template<class T>struct tag_t{using type=T;};
template<class T>using block_deduction=typename tag_t<T>::type;
template<class T, void(*func)(T)>
void Print(block_deduction<T> arg) {
这将强制调用者显式传递 T
,只是因为我认为在这里推导 T
会使代码有点脆弱(它必须与 func
的签名完全匹配).
我想创建一个使用类型的模板,并传递一个函数,该函数的模板参数包含该类型。
这是我目前拥有的:
#include <iostream>
void fooRef(int& ref) { std::cout << "In ref" << std::endl; }
void fooPtr(int* ptr) { std::cout << "In ptr" << std::endl; }
template<typename T, typename F>
void Print(T arg, F func) {
//DoABunchOfStuff();
func(arg);
//DoSomeMoreStuff();
}
int main() {
int x = 5;
int& ref = x;
Print<int*, void(*)(int*)>(&x, &fooPtr);
Print<int&, void(*)(int&)>(ref, &fooRef);
}
这行得通,但我觉得函数的调用者可能会有些冗长。理想情况下,我希望调用看起来像:
Print<int*, fooPtr>(ptr);
Print<int&, fooRef>(ref);
有没有办法简化打印函数的调用?
Is there a way to simplify the calls to the Print function?
是的。您所做的根本不是指定模板类型。函数模板经过一个过程调用 template argument deduction。在此过程中,传递给函数的参数会推导出它们的类型,编译器会尝试将其与模板参数相匹配。如果有效,则该函数将被清除,编译器将继续。所以如果我们使用
int main() {
int x = 5;
int& ref = x;
Print(&x, &fooPtr);
Print(std::ref(ref), &fooRef);
}
然后我们得到
In ptr
In ref
在第二次调用中,我使用了 std::ref
,因此它实际上会通过引用传递 ref
。否则它会复制 ref
所指的内容。
您可以使用模板参数推导和可变参数模板来简化它。方法如下:
template<typename FunctionType, typename... ArgumentTypes>
void Print(FunctionType function, ArgumentTypes... args) {
function(args...);
}
但是,如果您想正确支持引用,则必须求助于转发引用,这让编译器不仅可以推断出所发送参数的类型,而且也是值类型。
template<typename F, typename... Args>
void Print(F function, Args&&... args) {
function(std::forward<Args>(args)...);
}
现在一切似乎都很好,但您仍然需要处理接受任何参数的函数。限制您的函数只接受将使调用有效的参数可以使用尾随 return 类型来完成:
// instead of specifying the return type before the function name,
// let's place it at the end and use `decltype` to deduce the right type:
template<typename F, typename... Args>
auto Print(F function, Args&&... args) -> decltype(function(std::declval<Args>()...)) {
return function(std::forward<Args>(args)...);
}
使用此代码,您的编译器会出现更清晰的错误:
void func1(int) {}
Print(func1, 1); // works!
Print(func1, 1, 7); // error: no such function to call
如果您仍然希望 void
作为您的 return 类型并且仍然限制您的功能,您可以依靠 void_t
来做到这一点:
// void_t definition
template<typename...>
using void_t = void;
template<typename F, typename... Args>
auto Print(F function, Args&&... args) -> void_t<decltype(function(std::declval<Args>()...))> {
function(std::forward<Args>(args)...);
}
是的,您理想的语法很简单:
void fooRef(int& ref) { (void)ref; std::cout << "In ref" << std::endl; }
void fooPtr(int* ptr) { (void)ptr; std::cout << "In ptr" << std::endl; }
template<class T, void(*func)(T)>
void Print(T arg) {
//DoABunchOfStuff();
func(std::forward<Arg>(arg));
//DoSomeMoreStuff();
}
int main() {
int x = 5;
int& ref = x;
Print<int*, fooPtr>(&x);
Print<int&, fooRef>(ref);
}
添加
(void)ref
和 (void)ptr
以抑制未使用的变量警告。 std::forward<T>
用于提高一些极端情况的效率。我还建议在 T
上阻止扣除:
template<class T>struct tag_t{using type=T;};
template<class T>using block_deduction=typename tag_t<T>::type;
template<class T, void(*func)(T)>
void Print(block_deduction<T> arg) {
这将强制调用者显式传递 T
,只是因为我认为在这里推导 T
会使代码有点脆弱(它必须与 func
的签名完全匹配).