是否需要std::forward来完美传递函数调用结果至另一函数?
根据cppreference文档,若要在不显式存储的情况下将一个函数的调用结果完美传递给另一个函数,需要使用std::forward()。文档给出了如下示例:
例如,若一个包装器并非直接转发其参数,而是对参数调用成员函数并转发其结果:
// 转换包装器 template<class T> void wrapper(T&& arg) { foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get())); }
其中arg的类型可以是:
struct Arg { int i = 1; int get() && { return i; } // 右值调用时触发此重载 int& get() & { return i; } // 左值调用时触发此重载 };
尝试将右值转发为左值(例如使用左值引用类型T实例化形式(2))会导致编译错误。[...]
但实际测试发现,直接传递函数调用结果似乎也能完美保留值类型和值类别。以下是贴近示例的测试代码:
struct Inner{}; struct Arg { Inner inner{}; Inner&& get() && { return move(inner); } // 右值调用时触发此重载 Inner& get() & { return inner; } // 左值调用时触发此重载 const Inner& get() const & { return inner; }; // const左值调用时触发此重载 }; void foo(Inner&&) { std::cout << "&&" << std::endl; } void foo(Inner&) { std::cout << "&" << std::endl; } void foo(const Inner&) { std::cout << "const&" << std::endl; } template<class T> void wrapper(T&& arg) { // 注意:二选一测试! // 使用额外的forward foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get())); // 不使用额外的forward foo(forward<T>(arg).get()); } int main() { // prvalue wrapper(Arg{}); // xvalue Arg arg; wrapper(move(arg)); // lvalue Arg arg2; wrapper(arg2); // const lvalue const Arg arg3; wrapper(arg3); return 0; }
两种写法的输出完全一致:
&& && & const&
问题:实现函数调用结果向另一函数的完美传递,真的需要用std::forward()吗?
解答
不一定非要用std::forward,是否需要取决于函数调用结果的类型以及代码场景:
当返回值是引用类型时,直接传递即可
你的测试案例中,get()的返回值都是引用类型:Inner&、Inner&&、const Inner&。这类返回值对应的表达式自带明确的值类别:- 返回左值引用(
&/const&)的表达式是左值,会自然匹配foo(Inner&)或foo(const Inner&) - 返回右值引用(
&&)的表达式是xvalue,会自然匹配foo(Inner&&)
此时直接传递调用结果,值类别和类型信息都会被正确保留,不需要额外的std::forward。
- 返回左值引用(
当返回值是值类型或场景通用化时,
std::forward更稳妥
cppreference的例子中,get()的右值重载返回int(值类型),此时调用get()得到的是prvalue。虽然直接传递也能绑定到右值引用,但如果你的代码是通用模板(不确定get()返回值是值还是引用),使用std::forward<decltype(forward<T>(arg).get())>可以统一处理所有情况,确保不管返回类型是值还是引用,都能严格保留原表达式的属性,避免因返回类型变化导致的传递错误。
简单来说:如果场景中返回类型明确是引用,直接传就行;如果是通用模板或返回值可能为值类型,用std::forward能保证完美传递的通用性和正确性。
内容的提问来源于stack exchange,提问作者Mati

