在 C++ 11 中,如何调用任意可调用对象?
In c++ 11, how to invoke an arbitrary callable object?
可调用的概念定义在http://en.cppreference.com/w/cpp/concept/Callable。
假设我有一个可调用对象 f,它有一个参数类型 T* 和 return 类型 无效。 f 可以是任何可调用类型(函数对象、指向成员函数的指针、指向数据成员的指针等)。如何调用 f?
简单地调用 f(x) 失败,因为 f 可以是指向成员函数或数据成员的指针。有没有简单的方法调用f?一种可能的解决方案是 std::bind(f, x)(),但是当 f 有更多参数时,此解决方案变得更加复杂。
这正是 std::invoke
所做的,但在 C++17 之前它不会成为标准。你可以制作自己的版本,但如果它是完全通用的,它可能会非常复杂。
下面是两种情况的基本思路(代码取自cppreference.com):
template <class F, class... Args>
inline auto INVOKE(F&& f, Args&&... args) ->
decltype(std::forward<F>(f)(std::forward<Args>(args)...)) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template <class Base, class T, class Derived>
inline auto INVOKE(T Base::*pmd, Derived&& ref) ->
decltype(std::forward<Derived>(ref).*pmd) {
return std::forward<Derived>(ref).*pmd;
}
与其自己实施 INVOKE
,不如使用 library features that uses it. In particular, std::reference_wrapper
作品之一。因此,您可以使用 std::ref(f)(args...)
:
获得 std::invoke(f, args...)
的效果
template<typename F, typename... Args>
auto invoke(F f, Args&&... args)
-> decltype(std::ref(f)(std::forward<Args>(args)...))
{
return std::ref(f)(std::forward<Args>(args)...);
}
我没有转发f
因为std::reference_wrapper
要求传入的对象不是右值。使用 std::bind
而不是 std::ref
并不能解决问题。这意味着对于这样的函数对象:
struct F
{
void operator()() && {
std::cout << "Rvalue\n";
}
void operator()() const& {
std::cout << "Lvalue\n";
}
};
invoke(F{})
将打印 Lvalue
,而 C++17 中的 std::invoke(F{})
将打印 Rvalue
.
找到了技巧
#include <boost/hof/apply.hpp>
// ...
boost::hof::apply(f, args...);
boost::hof::apply
执行与 INVOKE
相同的操作。
或者,使用 boost::hana::apply
,其作用相同
可调用的概念定义在http://en.cppreference.com/w/cpp/concept/Callable。
假设我有一个可调用对象 f,它有一个参数类型 T* 和 return 类型 无效。 f 可以是任何可调用类型(函数对象、指向成员函数的指针、指向数据成员的指针等)。如何调用 f?
简单地调用 f(x) 失败,因为 f 可以是指向成员函数或数据成员的指针。有没有简单的方法调用f?一种可能的解决方案是 std::bind(f, x)(),但是当 f 有更多参数时,此解决方案变得更加复杂。
这正是 std::invoke
所做的,但在 C++17 之前它不会成为标准。你可以制作自己的版本,但如果它是完全通用的,它可能会非常复杂。
下面是两种情况的基本思路(代码取自cppreference.com):
template <class F, class... Args>
inline auto INVOKE(F&& f, Args&&... args) ->
decltype(std::forward<F>(f)(std::forward<Args>(args)...)) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template <class Base, class T, class Derived>
inline auto INVOKE(T Base::*pmd, Derived&& ref) ->
decltype(std::forward<Derived>(ref).*pmd) {
return std::forward<Derived>(ref).*pmd;
}
与其自己实施 INVOKE
,不如使用 library features that uses it. In particular, std::reference_wrapper
作品之一。因此,您可以使用 std::ref(f)(args...)
:
std::invoke(f, args...)
的效果
template<typename F, typename... Args>
auto invoke(F f, Args&&... args)
-> decltype(std::ref(f)(std::forward<Args>(args)...))
{
return std::ref(f)(std::forward<Args>(args)...);
}
我没有转发f
因为std::reference_wrapper
要求传入的对象不是右值。使用 std::bind
而不是 std::ref
并不能解决问题。这意味着对于这样的函数对象:
struct F
{
void operator()() && {
std::cout << "Rvalue\n";
}
void operator()() const& {
std::cout << "Lvalue\n";
}
};
invoke(F{})
将打印 Lvalue
,而 C++17 中的 std::invoke(F{})
将打印 Rvalue
.
#include <boost/hof/apply.hpp>
// ...
boost::hof::apply(f, args...);
boost::hof::apply
执行与 INVOKE
相同的操作。
或者,使用 boost::hana::apply
,其作用相同