如何在 c++17 中传递 Callable 对象以与 std::invoke 一起使用
How to pass a Callable object in c++17 to be used with std::invoke
为什么在将 i
传递给构造函数时以下内容无法编译。其他类似的构造编译。
#include <iostream>
#include <functional>
int RetXPrintU(int x, uint u)
{
std::cout << "RetXprintU(): " << u << std::endl;
return x;
}
template <typename Fn, typename... Args>
void Call(Fn&& fun, Args&&... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
template <typename Fn, typename... Args>
class CallableObj
{
public:
explicit CallableObj(Fn&& fun, Args&&... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
};
int main() {
int i = 4;
std::invoke(RetXPrintU, i, 8u);
Call(RetXPrintU, i, 8u);
CallableObj co(RetXPrintU, i, 8u); // WHY I DO NOT COMPILE?
//CallableObj co(RetXPrintU, 0, 8u); // WHY I COMPILE?
return 0;
}
如错误消息所述,编译器无法推断出 CallableObj
的模板参数。您需要为此添加一个推导指南:
template <typename Fn, typename... Args>
CallableObj(Fn&& fun, Args&&... args) -> CallableObj<Fn, Args>...>;
整个代码如下:
template <typename Fn, typename... Args>
class CallableObj
{
public:
explicit CallableObj(Fn fun, Args... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
};
template <typename Fn, typename... Args>
CallableObj(Fn&& fun, Args&&... args) -> CallableObj<Fn, Args...>;
正如@Jarod42 所指出的,没有必要使构造函数本身成为模板(就像在答案的第一个版本中那样)。
这里是 live example(由@Jarod42 更正的版本)。
我假设您的代码只是一个最小的示例,您需要 class 作为模板。如果不是这种情况,你最好选择其他解决方案。
这里的问题是您需要将模板从 class 移动到构造函数。当你有
template <typename Fn, typename... Args>
class CallableObj
{
public:
explicit CallableObj(Fn&& fun, Args&&... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
};
Args&&...
不是可变转发引用,因为它是针对 class 本身推导出来的,当您调用 CallableObj co(RetXPrintU, i, 8u);
时, class 被实例化并且构造函数被标记输出为
explicit CallableObj(int(int, unsigned int)&& fun, int&& arg1, unsigned int&& arg2)
我们要的是
class CallableObj
{
public:
template <typename Fn, typename... Args>
explicit CallableObj(Fn&& fun, Args&&... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
};
现在 Args
将在调用构造函数时被推导并且 Args&&
现在是转发引用。
CallableObj co(RetXPrintU, 0, 8u);
在您的示例中起作用的原因是因为 0
是纯右值并且纯右值可以绑定到右值引用。
为什么在将 i
传递给构造函数时以下内容无法编译。其他类似的构造编译。
#include <iostream>
#include <functional>
int RetXPrintU(int x, uint u)
{
std::cout << "RetXprintU(): " << u << std::endl;
return x;
}
template <typename Fn, typename... Args>
void Call(Fn&& fun, Args&&... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
template <typename Fn, typename... Args>
class CallableObj
{
public:
explicit CallableObj(Fn&& fun, Args&&... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
};
int main() {
int i = 4;
std::invoke(RetXPrintU, i, 8u);
Call(RetXPrintU, i, 8u);
CallableObj co(RetXPrintU, i, 8u); // WHY I DO NOT COMPILE?
//CallableObj co(RetXPrintU, 0, 8u); // WHY I COMPILE?
return 0;
}
如错误消息所述,编译器无法推断出 CallableObj
的模板参数。您需要为此添加一个推导指南:
template <typename Fn, typename... Args>
CallableObj(Fn&& fun, Args&&... args) -> CallableObj<Fn, Args>...>;
整个代码如下:
template <typename Fn, typename... Args>
class CallableObj
{
public:
explicit CallableObj(Fn fun, Args... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
};
template <typename Fn, typename... Args>
CallableObj(Fn&& fun, Args&&... args) -> CallableObj<Fn, Args...>;
正如@Jarod42 所指出的,没有必要使构造函数本身成为模板(就像在答案的第一个版本中那样)。
这里是 live example(由@Jarod42 更正的版本)。
我假设您的代码只是一个最小的示例,您需要 class 作为模板。如果不是这种情况,你最好选择其他解决方案。
这里的问题是您需要将模板从 class 移动到构造函数。当你有
template <typename Fn, typename... Args>
class CallableObj
{
public:
explicit CallableObj(Fn&& fun, Args&&... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
};
Args&&...
不是可变转发引用,因为它是针对 class 本身推导出来的,当您调用 CallableObj co(RetXPrintU, i, 8u);
时, class 被实例化并且构造函数被标记输出为
explicit CallableObj(int(int, unsigned int)&& fun, int&& arg1, unsigned int&& arg2)
我们要的是
class CallableObj
{
public:
template <typename Fn, typename... Args>
explicit CallableObj(Fn&& fun, Args&&... args)
{
std::invoke(std::forward<Fn>(fun), std::forward<Args>(args)...);
}
};
现在 Args
将在调用构造函数时被推导并且 Args&&
现在是转发引用。
CallableObj co(RetXPrintU, 0, 8u);
在您的示例中起作用的原因是因为 0
是纯右值并且纯右值可以绑定到右值引用。