invoke
在for_each2节中讲到for_each的调用函数invoke,在第for_each3节中,分析了一个关键宏CPP_auto_fun的实现,发现原来的代码中,Eric的例子返回值应该需要auto,给他提了patch. 这节回到invoke的分析。
operator ()
template<class F, class T, class T1, class... Args>
constexpr auto CPP_auto_fun(operator())(F T::*f, T1&& t1, Args&&... args)(const)
(
return (invoke_fn::coerce<T>(static_cast<T1&&>(t1), 0).*f)
(static_cast<Args&&>(args)...)
)
经过CPP_auto_fun的扩展
template<class F, class T, class T1, class... Args>
constexpr auto operator() (F T::*f, T1&& t1, Args&&... args)
const noexcept(noexcept(decltype((invoke_fn::coerce<T>(static_cast<T1&&>(t1), 0).*f) (static_cast<Args&&>(args)...))((invoke_fn::coerce<T>(static_cast<T1&&>(t1), 0).*f) (static_cast<Args&&>(args)...))))
-> decltype((invoke_fn::coerce<T>(static_cast<T1&&>(t1), 0).*f) (static_cast<Args&&>(args)...))
{
· return ((invoke_fn::coerce<T>(static_cast<T1&&>(t1), 0).*f) (static_cast<Args&&>(args)...));
}
这个的关键是coerce的作用; 要分析coerce 先要分析两个宏定义:
CPP_ret
#if CPP_CXX_CONCEPTS
#define CPP_ret(...) \
__VA_ARGS__ CPP_PP_EXPAND \
/**/
#else
#define CPP_ret \
CPP_broken_friend_ret \
/**/
#endif
定义concepts的时候,CPP_ret 被扩展为 arg, CPP_PP_EXPAND。 CPP_PP_EXPAND的定义为:
#define CPP_PP_EXPAND(...) __VA_ARGS__
相当于CPP_ret(X)(Y) 变成了X Y;
在没有定义concepts的时候:
CPP_broken_friend_ret的定义为:
#define CPP_broken_friend_ret(...) \
::concepts::detail::enable_if_t<__VA_ARGS__, \
CPP_BROKEN_FRIEND_RETURN_TYPE_AUX_ \
/**/
#define CPP_BROKEN_FRIEND_RETURN_TYPE_AUX_(...) \
CPP_BROKEN_FRIEND_RETURN_TYPE_AUX_3_(CPP_PP_CAT( \
CPP_TEMPLATE_AUX_2_, __VA_ARGS__)) \
/**/
#define CPP_TEMPLATE_AUX_2_requires
#define CPP_BROKEN_FRIEND_RETURN_TYPE_AUX_3_(...) \
static_cast<bool>(__VA_ARGS__) || CPP_false(::concepts::detail::xNil{})> \
以CPP_ret(T1 &&)(requires DerivedFrom<detail::decay_t, T>) 为例,
扩展完是:
::concepts::detail::enable_if_t<T1 &&, static_cast<bool>( DerivedFrom<detail::decay_t<T1>, T>) || CPP_false(::concepts::detail::xNil{})>
总结:CPP_ret模拟concept,只有在后面require满足的情况下,才返回前面指定的类型T1 &&。
meta::invoke
template <META_TYPE_CONSTRAINT(Invocable) Fn, typename... Args>
using invoke = typename Fn::template invoke<Args...>;
元编程模拟invoke函数,相当于把Args传给Fn里面的invoke。
enable_if_t
template<typename...>
struct identity
{
template<typename T>
using invoke = T;
};
template<typename T, bool Enable>
using enable_if_t = meta::invoke<identity<std::enable_if_t<Enable>>, T>;
作者改写了std的enable_if_t函数,把enable的标识放在了后面。
结合上面invoke的分析,这个函数的作用是将identity中的invoke赋值为T。
CPP_def
CPP_def
(
template(typename T, typename U)
concept WeaklyEqualityComparableWith_,
requires (detail::as_cref_t<T> t, detail::as_cref_t<U> u)
(
(t == u) ? 1 : 0,
(t != u) ? 1 : 0,
(u == t) ? 1 : 0,
(u != t) ? 1 : 0
)
);
结合上面的代码分析,最终解析成:
struct WeaklyEqualityComparableWith_Concept {
using Concept = WeaklyEqualityComparableWith_Concept;
template<typename T, typename U>
static auto Requires_ (detail::as_cref_t<T> t, detail::as_cref_t<U> u) -> ::concepts::detail::enable_if_t<int, static_cast<bool>
(::concepts::detail::requires_ <decltype((t == u) ? 1 : 0, (t != u) ? 1 : 0, (u == t) ? 1 : 0, (u != t) ? 1 : 0, void())>() )>;
template<typename T, typename U> struct Eval
{
template<typename C_ = Concept>
static constexpr decltype( &C_::template Requires_<T, U>, true) impl(int) noexcept
{
return true;
}
static constexpr bool impl(long) noexcept {
return false;
}
explicit constexpr operator bool() const noexcept
{
return Eval::impl(0);
}
constexpr auto operator!() const noexcept
{
return ::concepts::detail::Not<Eval>{};
}
template<typename That> constexpr auto operator&&(That) const noexcept
{
return ::concepts::detail::And<Eval, That>{};
}
};
};
template<typename T, typename U> constexpr bool WeaklyEqualityComparableWith_ = (bool)WeaklyEqualityComparableWith_Concept::Eval<T, U>{};
namespace lazy {
template<typename T, typename U>
constexpr auto WeaklyEqualityComparableWith_ = WeaklyEqualityComparableWith_Concept::Eval<T, U>{};
}
namespace defer {
using namespace lazy;
}
using _concepts_int_ = int;
总结:
CPP_def相当于定义了一个concepts。如上面的例子,如果不满足条件,返回WeaklyEqualityComparableWith_ 为false;如果满足条件返回WeaklyEqualityComparableWith_ 为true。
coerce
看一个简单的
struct invoke_fn
{
private:
template<class, class T1>
constexpr static decltype(auto) CPP_fun(coerce)(T1 && t1, long)( // 如果t1可以解引用,返回解引用后的值,
// 这样允许invoke的时候,传入对象的指针
noexcept(noexcept(*static_cast<T1 &&>(t1))) //
requires detail::Dereferenceable_<T1>)
{
return *static_cast<T1 &&>(t1);
}
template<class T, class T1>
constexpr static auto coerce(T1 && t1, int) noexcept -> CPP_ret(T1 &&)( // 这个允许基类的指针,传入子类的对象
requires DerivedFrom<detail::decay_t<T1>, T>)
{
return static_cast<T1 &&>(t1);
}
template<class, class T1>
constexpr static decltype(auto) CPP_fun(coerce)(T1 && t1, int)( // 如果是reference_wrapper_v, 先get value
noexcept(true) //
requires detail::is_reference_wrapper_v<detail::decay_t<T1>>)
{
return static_cast<T1 &&>(t1).get();
}
public:
// clang-format off
template<class F, class T, class T1, class... Args>
constexpr auto CPP_auto_fun(operator())(F T::*f, T1&& t1, Args&&... args)(const) // 允许通过类的指针调用有参数的情况
(
return (invoke_fn::coerce<T>(static_cast<T1&&>(t1), 0).*f)
(static_cast<Args&&>(args)...)
)
template<class D, class T, class T1>
constexpr auto CPP_auto_fun(operator())(D T::*f, T1&& t1)(const) // 允许通过类的指针调用类的变量
(
return invoke_fn::coerce<T>(static_cast<T1&&>(t1), 0).*f
)
template<class F, class... Args>
CPP_PP_IIF(RANGES_CONSTEXPR_INVOKE)(CPP_PP_EXPAND, CPP_PP_EAT)(constexpr)
auto CPP_auto_fun(operator())(F&& f, Args&&... args)(const) // 允许function + arg 的形式调用
(
return static_cast<F&&>(f)(static_cast<Args&&>(args)...)
)
// clang-format on
};
在C++中,函数的名字代表函数实现的功能,同名的函数一般是实现相同的功能(不然,就不是好的设计,代码)。不同的是,通过重载的方式实现,对不同参数的适配。所以,对于range这样优秀的代码,我们分析一个函数就可以明白,他实现的功能。
总结
上面的invoke说明了三种invoke 函数的情况:
- 通过类的指针,类的实例调用带参数的情况
- 通过类的指针,类的实例调用类的变量。
- 通过函数调用