【问题标题】:Perfect forwarding function parameters to function pointer: What about passing by value?完美转发函数参数到函数指针:值传递呢?
【发布时间】:2020-06-25 13:07:09
【问题描述】:

我有一个提供对其元素的访问的数据结构,以及一些用于迭代它们的循环逻辑。 我需要在循环中调用不同的函数。这些函数都将数据元素作为第一个参数,但允许有任意数量的附加参数。 到目前为止,这听起来像是完美转发的典型案例,所以我的尝试是这样的(通用示例):

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&amp;,然后我有一个inconsistent parameter pack deduction with 'int' and 'int&amp;'

DataStructure dataStruct;
int myInt {0};
looper( dataStruct, myFunc, myInt ); // <--- this line will cause a compiler error

这是一个非常具有描述性的错误消息,我知道我可以通过将myFunc 设置为const int&amp; 来解决它。

但是,我宁愿编写任何函数,它也可以按值获取参数,并将其指针传递给循环器。我怎样才能做到这一点?

【问题讨论】:

  • 为什么不使用基于范围的 for 循环或 ::std::for_each?请注意,您没有转发任何内容,因为您没有使用 Ts 的通用引用。
  • 我在这里给出的例子被大大简化了。实际上,元素的遍历比一个简单的循环更复杂,因此基于范围的 for 循环或std::for_each 都不会这样做。否则我当然会使用它们。关于你的第二点:我猜你是在引用函数指针签名?那不能采用普遍的参考。但就在下面一行中,looper 实际采用的参数被视为通用引用。
  • 请注意,此处可能不适合完美转发。如果在循环的第一次迭代中调用的函数从它的一个参数移动,则该对象的数据将不可用于循环的第二次迭代中的调用。对于这样的事情,最好使用一个好的 ol' lvalue-reference-to-const 代替。
  • 啊,好点子,谢谢。移动在实际应用程序中没有意义,因此不会发生,但我仍然希望我的代码能够反映意图。

标签: c++ function-pointers variadic-templates perfect-forwarding


【解决方案1】:

Args 将被推导出两次,一次用于函数签名,一次用于参数。它们不一定需要完全相同:参数推导出为int&amp;,因为它是从局部变量推导出来的,但它不对应于函数签名(即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&amp;,则不能传递const int&amp;。这可能会导致令人困惑的错误消息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-03
    • 2016-11-07
    • 2018-08-25
    • 1970-01-01
    • 1970-01-01
    • 2012-11-28
    • 2020-11-08
    相关资源
    最近更新 更多