boost :: range ::在一个自定义调用中加入多个范围

时间:2016-06-14 00:15:47

标签: c++ boost iterator variadic-templates perfect-forwarding

gnzlbg中的解决方案部分问题boost::range::join for multiple ranges意味着它可以在一个客户端代码调用中将多个范围连接到调用的自定义函数可变参数模板boost::joinboost::make_iterator_range。根据该问题,答案和评论,先验可以加入2个范围,后者需要确保使用先验的非const超载。第二个之后的任何容器都应该通过std::forward完美转发。但我的客户端代码只能使用最多3个参数成功调用它。还有更多的东西无法编译。怎么了?怎么解决?现在有没有加入许多范围的Boost实体?

我已经复制并粘贴了OP的实现,只在这里编辑它以获取空白可读性并添加相关标题:

#include <utility>
#include <boost/range/join.hpp>

template<class C>
auto join(C&& c)
-> decltype(boost::make_iterator_range(std::begin(c), std::end(c)))
{
    return boost::make_iterator_range(std::begin(c), std::end(c));
}

template<class C, class D, class... Args>
auto join(C&& c, D&& d, Args&&... args)
-> decltype
(
    boost::join
    (
        boost::join
        (
            boost::make_iterator_range(std::begin(c), std::end(c)),
            boost::make_iterator_range(std::begin(d), std::end(d))
        ),
        join(std::forward<Args>(args)...)
    )
)
{
    return boost::join
    (
        boost::join
        (
            boost::make_iterator_range(std::begin(c), std::end(c)),
            boost::make_iterator_range(std::begin(d), std::end(d))
        ),
        join(std::forward<Args>(args)...)
    );
}

并添加了我的客户端代码:

#include <deque>
#include <array>
#include <vector>
#include <iostream>

int main()
{
    std::deque<int> deq { 0, 1, 2, 3, 4 };
    std::array<int, 4> stl_arr { 5, 6, 7, 8 };
    int c_arr[3] { 9, 10, 11 };
    std::vector<int> vec { 12, 13 };

    for (auto& i : join(deq, stl_arr, c_arr))
    {
        ++i;
        std::cout << i << ", ";         // OK, prints 1 thru 12
    }

    //join(deq, stl_arr, c_arr, vec);   // COMPILER ERROR
}

1 个答案:

答案 0 :(得分:5)

有两件事正在发生。第一个是以下声明不能按预期工作:

template<class C>
auto join(C&& c)
-> decltype(boost::make_iterator_range(std::begin(c), std::end(c)));

template<class C, class D, class... Args>
auto join(C&& c, D&& d, Args&&... args)
-> decltype
(
    boost::join
    (
        boost::join
        (
            boost::make_iterator_range(std::begin(c), std::end(c)),
            boost::make_iterator_range(std::begin(d), std::end(d))
        ),
        join(std::forward<Args>(args)...)
    //  ^^^^-- (1)
    )
);

问题的关键在于,在点(1)join的第二次重载不在范围内。有三个参数没有问题显示,因为Args包的长度为1,因此生成的join(std::forward<Arg0>(arg0))扩展使用范围内 的第一个重载。

使用四个或更多参数时,生成的join(std::forward<Arg0>(arg0), ..., std::forward<ArgN>(argN))扩展需要第二个重载,但它不在selfsame的后期返回类型的范围内。

解决这个问题的一种方法是将一组函数模板转换为一组成员函数模板,因为类范围更宽松:

struct join_type {
    template<class C>
    auto operator()(C&& c) const
    -> decltype(boost::make_iterator_range(std::begin(c), std::end(c)))
    {
        return boost::make_iterator_range(std::begin(c), std::end(c));
    }

    template<class C, class D, class... Args>
    auto operator()(C&& c, D&& d, Args&&... args) const
    -> decltype
    (
        boost::join
        (
            boost::join
            (
                boost::make_iterator_range(std::begin(c), std::end(c)),
                boost::make_iterator_range(std::begin(d), std::end(d))
            ),
            (*this)(std::forward<Args>(args)...)
        )
    )
    {
        return boost::join
        (
            boost::join
            (
                boost::make_iterator_range(std::begin(c), std::end(c)),
                boost::make_iterator_range(std::begin(d), std::end(d))
            ),
            (*this)(std::forward<Args>(args)...)
        );
    }
};

constexpr join_type join {};

请注意,重要的是类范围,而不是我们选择使用operator()作为成员函数模板的名称。您也可以使用名为foo的静态成员函数模板,并在类之外使用正常的函数模板转发给它。

现在我们可以揭示第二个问题,即实现是错误的,只能使用奇数个参数!即使join(a, b)也行不通,但ADL之前可能已经隐藏了该错误(即客户端最终会有效地调用boost::join(a, b),这显然有效)( Live On Coliru )。

让我们改写折叠:

struct join_type {
    template<class C>
    auto operator()(C&& c) const
    -> decltype(boost::make_iterator_range(begin(c), end(c)))
    {
        return boost::make_iterator_range(begin(c), end(c));
    }

    template<typename First, typename Second, typename... Rest>
    auto operator()(First&& first, Second&& second, Rest&&... rest) const
    -> decltype( (*this)(boost::join(boost::make_iterator_range(begin(first), end(first)), boost::make_iterator_range(begin(second), end(second))), std::forward<Rest>(rest)...) )
    {
        return (*this)(boost::join(boost::make_iterator_range(begin(first), end(first)), boost::make_iterator_range(begin(second), end(second))), std::forward<Rest>(rest)...);
    }
};

constexpr join_type join {};

Live On Coliru