Oven移植のレビュー状況

id:iorateさんから指摘されて、regular()関数が出力する関数オブジェクトindirect_functorに対するboost::result_ofの特殊化で、boost::result_ofに加えて、boost::result_ofに対する特殊化を追加しました。Boost.Phoenixのbind()がresult_ofにconstを渡すのだそうです( http://ideone.com/EcNT9 )。

namespace boost {
#if !defined(BOOST_RESULT_OF_USE_DECLTYPE) || defined(BOOST_NO_DECLTYPE)
    template <class F>
    struct result_of<boost::range::detail::indirect_functor<F>()> {
        typedef typename boost::result_of<
            typename boost::range::detail::indirect_functor<F>::indirected_functor_type()
        >::type type;
    };

    template <class F>
    struct result_of<const boost::range::detail::indirect_functor<F>()> {
        typedef typename boost::result_of<
            typename boost::range::detail::indirect_functor<F>::indirected_functor_type()
        >::type type;
    };
#endif
    template <class F>
    struct tr1_result_of<boost::range::detail::indirect_functor<F>()> {
        typedef typename boost::tr1_result_of<
            typename boost::range::detail::indirect_functor<F>::indirected_functor_type()
        >::type type;
    };
    
    template <class F>
    struct tr1_result_of<const boost::range::detail::indirect_functor<F>()> {
        typedef typename boost::tr1_result_of<
            typename boost::range::detail::indirect_functor<const F>::indirected_functor_type()
        >::type type;
    };
}


それと、C++11環境でas_containerを代入で使用すると、initializer_listとのオーバーロードで曖昧になってしまい、コンパイルエラーになる問題を修正しました。

#include <iostream>
#include <vector>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/experimental/as_container.hpp>

bool is_odd(int x) { return x % 2 == 0; }

int main()
{
    const std::vector<int> v1 = {1, 2, 3, 4, 5};

    std::vector<int> v2;
    v2 = v1 | boost::adaptors::filtered(is_odd) | boost::as_container; // これ

    for (int x : v2) {
        std::cout << x << std::endl;
    }
}
2
4

対処の内容としては、テンプレートの型変換演算子で、関数テンプレートのデフォルトテンプレート引数を使用してinitiailzier_listをdisable_ifで弾くようにしました。

template <class>
struct is_initializer_list : mpl::false_ {};

template <class T>
struct is_initializer_list<std::initializer_list<T> > : mpl::true_ {};

template <class Range>
class as_container_wrapper {
    Range range_;
public:
    explicit as_container_wrapper(Range& range)
        : range_(range) {}

#if !defined(BOOST_NO_INITIALIZER_LISTS) && !defined(BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS)
    template <class Container,
              class Enable = typename boost::disable_if<is_initializer_list<Container>>::type>
    operator Container() const
    {
        return Container(::boost::begin(range_), ::boost::end(range_));
    }
#else
    template <class Container>
    operator Container() const
    {
        return Container(::boost::begin(range_), ::boost::end(range_));
    }
#endif
};

この対処の問題としては、initializer_listと関数テンプレートのデフォルトテンプレート引数が同時に使えなければ、initializer_listとの曖昧さを解決できないというのがあります。今後C++11対応するコンパイラが、関数テンプレートのデフォルトテンプレート引数より先にinitializer_listを実装してしまった場合に問題が起こります。
テンプレートの型変換演算子で、他にSFINAEする方法があればいいのですが、現状ではその方法が思いつきません。


また、以下のようなコンストラクタの()による呼び出しでは、as_containerのオーバーロードが曖昧でコンパイルエラーになってしまうという問題。

std::vector<int> v2(v1 | as_container); // error

oven::copiedのドキュメントによれば、どうやらGCCのバグらしいです。まだ詳しく調べてません。