Как использовать диапазон на основе for_each в Boost.Phoenix?

Давайте рассмотрим следующий код:

#include <boost/phoenix.hpp>
#include <algorithm>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> const small_ints {1, 2, 3, 4, 5};
    std::vector<int> const big_ints {11, 12, 13, 14, 15};

    namespace phoenix = boost::phoenix;
    using namespace boost::phoenix;
    using namespace boost::phoenix::placeholders;

    std::vector<int>::const_iterator big_ints_it;
    std::for_each(small_ints.cbegin(), small_ints.cend(),
        for_(phoenix::ref(big_ints_it) = begin(phoenix::cref(big_ints)), phoenix::ref(big_ints_it) != end(phoenix::cref(big_ints)), ++phoenix::ref(big_ints_it))
        [
            std::cout << val('(') << arg1 << ',' << *phoenix::ref(big_ints_it) << "), "
        ]
    );
}

Работает нормально, печатая ожидаемую последовательность пар

(1,11), (1,12), (1,13), (1,14), (1,15), (2,11), (2,12), …, (5,14), (5,15),

это просто декартово произведение, полученное в результате двойной итерации.

Тем не менее for_ некрасиво Посмотри на все эти ref, cref (что-то я всегда должен явно выбирать из phoenix в противном случае возникают конфликты с std версии). Не говоря уже о том, что я должен оставаться бесполезным big_ints_it переменная!

Также обратите внимание, что это следует примеру, предоставленному самой документацией Phoenix.

Теперь я попытался использовать for_each упоминается вдоль итерационных алгоритмов, ожидающих гораздо более простую версию:

std::for_each(small_ints.cbegin(), small_ints.cend(),
    for_each(phoenix::cref(big_ints),
        std::cout << val('(') << arg1 << ", " << arg2 << "), "
    )
);

но он даже не компилируется! Или хотя бы с Visual Studio 2013 и Boost 1.61.

Я получаю несколько ошибок. Первый из которых

1>c:\programming\boost_1_61_0\boost\proto\detail\poly_function.hpp(205): error C2039: 'type' : is not a member of 'boost::proto::functional::at::result<Sig>'
1>          with
1>          [
1>              Sig=boost::proto::functional::at (boost::phoenix::vector2<const boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::phoenix::detail::tag::function_eval,boost::proto::argsns_::list3<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<boost::phoenix::impl::for_each>,0>,boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<boost::reference_wrapper<const std::vector<int,std::allocator<int>>>>,0>>,boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<std::basic_ostream<char,std::char_traits<char>> &>,0>>,boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<char>,0>>>,2>>,boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<boost::phoenix::argument<1>>,0>>>,2>>,boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<char [3]>,0>>>,2>>,boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<boost::phoenix::argument<2>>,0>>>,2>>,boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<char [4]>,0>>>,2>>>,3>> *,const int &> &,boost::mpl::int_<2>)
1>          ]
…

Я даже не могу ввести всю первую ошибку, так как Stackru говорит, что тело вопроса ограничено 30000 символов, из которых я ввел 135 034 из них…

1 ответ

Решение

Проблема в том, что вы используете arg2 в вашем внутреннем выражении. Давайте посмотрим на ситуацию.

Вы в основном имеете:

- for_each(begin,end,outer_expr);
- outer_expr=for_each(begin,end,inner_expr);

И то и другое outer_expr а также inner_expr являются лямбда-выражением, которое принимает один аргумент (элемент его соответствующих векторов).

То, что вам нужно, это способ передачи аргумента от outer_expr в inner_expr, К счастью, Boost.Phoenix предоставляет простой способ сделать это: boost::phoenix::lambda:

std::for_each(small_ints.cbegin(), small_ints.cend(),
            phx::for_each(phx::cref(big_ints),
            phx::lambda(_a=arg1)[phx::ref(std::cout) << '(' << _a << ", " << arg1 << "), "]
        )
    );

Или немного глупо, но, возможно, помогает читабельности:

boost::phoenix::placeholders::arg1_type current_small_int, current_big_int;
boost::phoenix::local_names::_a_type passed_small_int;

std::for_each(small_ints.cbegin(), small_ints.cend(),
            phx::for_each(phx::cref(big_ints),
            phx::lambda(passed_small_int=current_small_int)[phx::ref(std::cout) << '(' << passed_small_int << ", " << current_big_int << "), "]
        )
    );

Полный образец (работает на WandBox)

#include <iostream>
#include <algorithm>
#include <vector>
#include <boost/phoenix.hpp>


int main()
{
    std::vector<int> const small_ints {1, 2, 3, 4, 5};
    std::vector<int> const big_ints {11, 12, 13, 14, 15};

    namespace phx = boost::phoenix;
    using boost::phoenix::placeholders::arg1;
    using boost::phoenix::local_names::_a;

    std::vector<int>::const_iterator big_ints_it;
    std::for_each(small_ints.cbegin(), small_ints.cend(),
        phx::for_each(phx::cref(big_ints),
        phx::lambda(_a=arg1)[phx::ref(std::cout) << '(' << _a << ", " << arg1 << "), "]
    )
    );
}

PS: Если ваш компилятор может инициализировать векторы таким образом, он, вероятно, сможет использовать реальные лямбды, и это, вероятно, приведет к уменьшению головной боли.

Другие вопросы по тегам