c++ - boost::range::join 一个自定义调用中的多个范围

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

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

我已经复制并粘贴了该 OP 的实现,在此处对其进行编辑只是为了 boost 空白可读性并添加相关标题:

#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
}

最佳答案

有两件事正在发生。首先是以下声明不会按预期工作:

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 pack 的长度为 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 )。

让我们重写 fold :

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

关于c++ - boost::range::join 一个自定义调用中的多个范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37801073/

相关文章:

Java从多次存在的config.properties文件中获取某个属性

C++ 从对象 vector 中选择

c++ - 创建模板参数的默认值变量

c++ - 错误C2676:std::set::const_iterator没有运算符+功能?

c++ - boost::asio read - 在从套接字读取的所有数据之后返回,不等待 EOF

c++ - 可以将 boost 累加器用作类成员

c++ - 关于模板函数中 vector 迭代器的问题

c++ - 使用 Google 的 protobuf 时出错

c++ - 在 C++ 中使用 FFTW 求解一维热方程

c++ - boost 异步服务器两次接受连接