transformed後にfilteredすると変換関数が2回呼ばれる

filtered dereferences underlying range elements twice


filteredの中で、イテレータの指す先が条件を満たすかをチェックするために間接参照し、その間接参照のたびにtransformedの変換関数が呼ばれるので、変換関数が多く呼ばれてしまうという問題。

#include <iostream>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/for_each.hpp>

int add(int x)
{
    std::cout << "add function : " << x << std::endl;
    return x + 1;
}

bool pred(int x)
{
    return true;
}

void disp(int x)
{
    std::cout << x << std::endl;
}

int main()
{
    using namespace boost::adaptors;

    const int ar[] = {1, 2, 3};

    // 3要素なのでadd関数が3回呼ばれることを期待する
    boost::for_each(ar | transformed(add) | filtered(pred), disp);
}
add function : 1
add function : 1
2
add function : 2
add function : 2
3
add function : 3
add function : 3
4

各要素に対して2回ずつadd関数が呼ばれてる。
これは仕組み上仕方がないので、メモ化しましょう。oven::memoizedです。

#include <iostream>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/for_each.hpp>
#include <pstade/oven/memoized.hpp>

int add(int x)
{
    std::cout << "add function : " << x << std::endl;
    return x + 1;
}

bool pred(int x)
{
    return true;
}

void disp(int x)
{
    std::cout << x << std::endl;
}

int main()
{
    using namespace boost::adaptors;
    using pstade::oven::memoized;

    const int ar[] = {1, 2, 3};

    // 3要素なのでadd関数が3回呼ばれることを期待する
    boost::for_each(ar | transformed(add) | memoized | filtered(pred), disp);
}
add function : 1
2
add function : 2
3
add function : 3
4

すっきり。