我有一个数据结构,它提供对其元素的访问,以及一些循环逻辑,用于如何迭代它们。 我需要在循环中调用不同的函数。 这些函数都以一个数据元素作为第一个参数,但应允许有任意数量的附加参数。 到目前为止,这听起来像是完美fowarding的典型案例,所以我的尝试是这样的(通用示例):
template<typename ... Ts>
void looper(
const DataStructure& dataStruct,
void (*func)(const DataElement&, Ts ...),
Ts&& ... args
){
for (Index i{0}; i<dataStruct.someSize(); ++i )
func( dataStruct.elem(i), std::forward<Ts>(args) ... );
}
但是,假设我想用一个按值取参数的函数来调用它(像基元类型),那么我很快就出现了问题。
void myFunc( const DataElement&, int ){
/* do something */
}
如果我调用looper函数并传递一个int
类型的变量,它将始终被识别为int&;
,然后我就有了一个不一致的参数包推导,其中有'int'和'int&;'
:
DataStructure dataStruct;
int myInt {0};
looper( dataStruct, myFunc, myInt ); // <--- this line will cause a compiler error
这是一个描述性很强的错误消息,我知道,例如,我可以通过使myfunc
取一个const int&;
来解决它。
然而,我宁愿能够编写任何函数,这些函数也可以通过值获取参数,并将其指针传递给循环程序。 我怎么才能做到呢?
args
将推导两次,一次用于函数签名,一次用于参数。 它们不一定需要完全相同:参数被推论为int&;
,因为它是从局部变量推论出来的,但它并不对应于函数签名(即int
)。
你可以拆分它们,像这样:
template<typename... Ts_fun_args, typename... Ts_param_args>
void looper(
const DataStructure& dataStruct,
void (*func)(const DataElement&, Ts_fun_args ...),
Ts_param_args&& ... args
){
for (Index i{0}; i<dataStruct.someSize(); ++i )
func( dataStruct.elem(i), std::forward<Ts_param_args>(args) ... );
}
注意,TS_FUN_ARGS
需要可转换为TS_PARAM_ARGS
。 例如,如果函数签名采用int&;
,则不能传递常量int&;
。 这可能会导致混乱的错误消息。