应如何将二元谓词传递给用户定义的 Boost.MPL 算法?
考虑以下对 std::any_of
的 Boost.MPL 风格元编程版本的尝试,
#include <iostream> // cout
#include <type_traits> // is_base_of, is_pod
#include <boost/mpl/apply.hpp> // apply
#include <boost/mpl/fold.hpp> // fold
#include <boost/mpl/lambda.hpp> // lambda, _1, _2
#include <boost/mpl/logical.hpp> // and_, true_
#include <boost/mpl/vector.hpp> // vector
template
<
typename Sequence,
typename Pred
>
struct all_of
:
boost::mpl::fold<
Sequence,
boost::mpl::true_,
boost::mpl::lambda<
boost::mpl::and_<
boost::mpl::_1,
boost::mpl::apply< Pred, boost::mpl::_2 >
>
>
>
{};
typedef int P1; typedef char P2; typedef float P3;
typedef boost::mpl::vector<
P1, P2, P3
> pod_types;
struct B {}; struct D1: B {}; struct D2: B {}; struct D3: B {};
typedef boost::mpl::vector<
D1, D2, D3
> derived_types;
int main()
{
std::cout << (std::is_pod<P1>::value) << '\n'; // true
std::cout << (std::is_pod<P2>::value) << '\n'; // true
std::cout << (std::is_pod<P3>::value) << '\n'; // true
std::cout << (
all_of<
pod_types,
std::is_pod< boost::mpl::_1 >
>::type::value // true
) << '\n';
std::cout << (std::is_base_of<B, D1>::value) << '\n'; // true
std::cout << (std::is_base_of<B, D2>::value) << '\n'; // true
std::cout << (std::is_base_of<B, D3>::value) << '\n'; // true
std::cout << (
all_of<
derived_types,
std::is_base_of< B, boost::mpl::_1 >
>::type::value // false (but should be true)
) << '\n';
return 0;
}
它打印出:1 1 1 1 1 1 1 0。即,对 all_of
的最终调用> 作为谓词传递的 std::is_base_of
会生成 false。为什么这不起作用?显然,基类 B
没有正确绑定到谓词。我应该如何传递二元谓词? mpl::lambda 或 mpl::bind 的某种组合?
更新
基于 Luc Touraille 的出色回答,这是我的问题的无 lambda 解决方案,还有一个额外的好处是 none_of
和 any_of 的编译时版本
template<typename Sequence, typename Pred>
struct all_of
:
std::is_same< typename
boost::mpl::find_if<
Sequence,
boost::mpl::not_<Pred>
>::type, typename
boost::mpl::end<Sequence>::type
>
{};
template<typename Sequence, typename Pred>
struct none_of
:
all_of< Sequence, boost::mpl::not_< Pred > >
{};
template<typename Sequence, typename Pred>
struct any_of
:
boost::mpl::not_< none_of< Sequence, Pred > >
{};
Consider the following attempt at a Boost.MPL style metaprogramming version of std::any_of
#include <iostream> // cout
#include <type_traits> // is_base_of, is_pod
#include <boost/mpl/apply.hpp> // apply
#include <boost/mpl/fold.hpp> // fold
#include <boost/mpl/lambda.hpp> // lambda, _1, _2
#include <boost/mpl/logical.hpp> // and_, true_
#include <boost/mpl/vector.hpp> // vector
template
<
typename Sequence,
typename Pred
>
struct all_of
:
boost::mpl::fold<
Sequence,
boost::mpl::true_,
boost::mpl::lambda<
boost::mpl::and_<
boost::mpl::_1,
boost::mpl::apply< Pred, boost::mpl::_2 >
>
>
>
{};
typedef int P1; typedef char P2; typedef float P3;
typedef boost::mpl::vector<
P1, P2, P3
> pod_types;
struct B {}; struct D1: B {}; struct D2: B {}; struct D3: B {};
typedef boost::mpl::vector<
D1, D2, D3
> derived_types;
int main()
{
std::cout << (std::is_pod<P1>::value) << '\n'; // true
std::cout << (std::is_pod<P2>::value) << '\n'; // true
std::cout << (std::is_pod<P3>::value) << '\n'; // true
std::cout << (
all_of<
pod_types,
std::is_pod< boost::mpl::_1 >
>::type::value // true
) << '\n';
std::cout << (std::is_base_of<B, D1>::value) << '\n'; // true
std::cout << (std::is_base_of<B, D2>::value) << '\n'; // true
std::cout << (std::is_base_of<B, D3>::value) << '\n'; // true
std::cout << (
all_of<
derived_types,
std::is_base_of< B, boost::mpl::_1 >
>::type::value // false (but should be true)
) << '\n';
return 0;
}
This prints out: 1 1 1 1 1 1 1 0. I.e., the final call to all_of
with std::is_base_of
passed as a predicate generates false. Why does this not work? Apperently, the base class B
does not get properly bound to the predicate. How should I pass a binary predicate? Some combination of mpl::lambda or mpl::bind?
UPDATE
Based on Luc Touraille's excellent answer, here is the lambda-free solution to my question, with as an added bonus the compile-time versions of none_of
and any_of
template<typename Sequence, typename Pred>
struct all_of
:
std::is_same< typename
boost::mpl::find_if<
Sequence,
boost::mpl::not_<Pred>
>::type, typename
boost::mpl::end<Sequence>::type
>
{};
template<typename Sequence, typename Pred>
struct none_of
:
all_of< Sequence, boost::mpl::not_< Pred > >
{};
template<typename Sequence, typename Pred>
struct any_of
:
boost::mpl::not_< none_of< Sequence, Pred > >
{};
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是使用
find_if 的解决方案
而不是fold
:如果你想坚持
fold
,您需要使用lambda
之前调用它来保护参数:但是,您会注意到,这也行不通。我不确定到底为什么,我认为这与 此讨论有关Boost 邮件列表。显然,
apply
的数量存在问题,它大于lambda
支持的数量。无论如何,一个简单的解决方法是使用apply1
而不是apply
。这是完整的解决方案:Here is a solution using
find_if
instead offold
:If you want to stick to
fold
, you'll need to turn the predicate into a metafunction usinglambda
before invoking it, to protect the arguments:However, you'll note that this won't work either. I'm not sure why exactly, I think it is related to this discussion on the Boost mailing list. Apparently, there is an issue with the arity of
apply
which is greater than the one supported bylambda
. Anyway, a simple workaround is to useapply1
instead ofapply
. Here is the full solution:您需要将谓词转换为 lambda,否则 _1 将被解释为折叠的第一级调用,而不是传递给 Pred 的第一个参数。 mpl::lambda 就是你需要的
You need to turn the predicate into a lambda or _1 will be interpreted as the first level of calls of the fold instead of the first parameter to be passed to Pred. mpl::lambda is what you need