我需要在某些C 11/14代码中使用INVOKE语义(由C 17中的std :: invoke实现).我当然不想自己实施,我认为这将是一场灾难.所以我决定利用现有的标准库设施.很快就想到了:
template<typename Fn, typename... Args>
constexpr decltype(auto) my_invoke(Fn&& f, Args&&... args)
noexcept(noexcept(std::bind(std::forward<Fn>(f), std::forward<Args>(args)...)()))
{
return std::bind(std::forward<Fn>(f), std::forward<Args>(args)...)();
}
这种实现的一个问题是它无法区分左值和右值(例如,如果函数对象在operator()()&和operator()()&&上重载,只有&&版本永远被称为).是否有一些库实用程序也完美转发可调用程序本身?如果没有,那么实施它的好方法是什么? (例如转发包装器).
最佳答案 INVOKE的所有特殊案例都是关于成员的指针.只需SFINAE并将它们发送到mem_fn.
template<typename Fn, typename... Args,
std::enable_if_t<std::is_member_pointer<std::decay_t<Fn>>{}, int> = 0 >
constexpr decltype(auto) my_invoke(Fn&& f, Args&&... args)
noexcept(noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
{
return std::mem_fn(f)(std::forward<Args>(args)...);
}
template<typename Fn, typename... Args,
std::enable_if_t<!std::is_member_pointer<std::decay_t<Fn>>{}, int> = 0>
constexpr decltype(auto) my_invoke(Fn&& f, Args&&... args)
noexcept(noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
{
return std::forward<Fn>(f)(std::forward<Args>(args)...);
}
这基本上是N4169中提出的最小实现.(真正的标准库实现者不这样做,因为将INVOKE功能集中在一个地方并且让各种其他部分调用它可以更加可维护.)
顺便说一下,使用std :: bind是完全错误的.它复制/移动所有参数,将它们作为lvalues传递给callable,并使用reference_wrappers,占位符和绑定表达式做出不必要的魔法.