C++中std::invoke能否自动匹配左值/右值引用重载?
std::invoke 与重载函数、完美转发的问题
示例代码
#include <iostream> #include <functional> using namespace std; int func_invk_lr(int& a) { cout << "call left" << endl; return a; } int func_invk_lr(int&& a) { cout << "call right" << endl; return a; } template<typename T> void call_invk_lr(T&& a) { std::invoke(func_invk_lr, a); // 错误:无法解析重载函数类型 std::invoke(func_invk_lr, std::forward<T>(a)); // 错误:无法解析重载函数类型 } int main() { int a = 1; std::invoke(func_invk_lr, a); // 错误:无法解析重载函数类型 std::invoke(func_invk_lr, 1); // 错误:无法解析重载函数类型 call_invk_lr(a); call_invk_lr(1); }
疑问
- std::invoke是否能够识别左值或右值实参,进而调用对应的重载函数?
- 若std::invoke可按预期工作,在模板函数的
T&&参数场景下,是否仍需使用std::forward传递参数?
解答
问题1:std::invoke能否识别左右值实参调用对应重载?
代码报错的核心并非std::invoke无法识别左右值,而是编译器无法确定要传入std::invoke的func_invk_lr具体是哪个重载版本。std::invoke的第一个参数需要明确的可调用对象类型,而重载函数名本身属于未解析的重载类型,编译器无法自动推导匹配的重载。
只要帮编译器明确指定重载版本,std::invoke完全可以根据实参的左右值属性调用对应重载。示例:
int a = 1; // 明确指定左值引用版本的重载 std::invoke(static_cast<int(&)(int&)>(func_invk_lr), a); // 输出 call left // 明确指定右值引用版本的重载 std::invoke(static_cast<int(&)(int&&)>(func_invk_lr), 1); // 输出 call right
问题2:模板T&&场景下是否需要std::forward?
当std::invoke能正常工作(即已明确重载版本),模板的T&&参数场景下必须使用std::forward<T>(a)传递参数,以此实现完美转发:
- 若传入左值,
std::forward保留其左值属性,确保std::invoke调用左值版本重载; - 若传入右值,
std::forward还原其右值属性,确保std::invoke调用右值版本重载。
修改后的模板函数示例(通过lambda让编译器自动推导重载):
template<typename T> void call_invk_lr(T&& a) { std::invoke([](auto&& arg) { return func_invk_lr(std::forward<decltype(arg)>(arg)); }, std::forward<T>(a)); }
此时调用call_invk_lr(a)会触发左值重载,调用call_invk_lr(1)会触发右值重载,完美转发的作用在这里不可或缺。
内容的提问来源于stack exchange,提问作者Zeyu Zhang CN
相关产品推荐
相关产品推荐

