boost::range::join 一次自定义调用中的多个范围
boost::range::join many ranges in one custom call
gnzlbg's SO question boost::range::join for multiple ranges 中的 Solution 部分暗示它可以在一个客户端代码调用中加入多个范围,调用一个自定义函数可变参数模板,该模板调用 boost::join
和 boost::make_iterator_range
。根据该问题、答案和评论,先验可以加入 2 个范围,并且需要后者以确保使用先验的非 const
重载。第二个容器之后的任何容器都应该是 perfect-forwarded via std::forward
。但是我的客户端代码最多只能使用 3 个参数成功调用它。任何更多的东西都无法编译。出了什么问题以及如何解决?现在有没有加入许多范围的Boost实体?
我已经复制并粘贴了该 OP 的实现,在此处对其进行编辑只是为了提高空白可读性并添加相关的 headers:
#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
包的长度为 1,因此生成的 join(std::forward<Arg0>(arg0))
扩展使用第一个重载, 在范围内是 。
如果有四个或更多参数,生成的 join(std::forward<Arg0>(arg0), ..., std::forward<ArgN>(argN))
扩展需要第二次重载,但它不在 selfsame 的后期 return 类型的范围内。
解决这个问题的一种方法是将函数模板集转换为成员函数模板集,因为 class 范围更宽松:
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 {};
请注意,重要的是 class 范围,而不是我们选择使用 operator()
作为成员函数模板的名称。您也可以使用名为 foo
的静态成员函数模板,并在转发给它的 class 之外有一个普通函数模板。
现在我们可以揭露第二个问题,因为实现有问题并且只能处理奇数个参数!即使 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 {};
gnzlbg's SO question boost::range::join for multiple ranges 中的 Solution 部分暗示它可以在一个客户端代码调用中加入多个范围,调用一个自定义函数可变参数模板,该模板调用 boost::join
和 boost::make_iterator_range
。根据该问题、答案和评论,先验可以加入 2 个范围,并且需要后者以确保使用先验的非 const
重载。第二个容器之后的任何容器都应该是 perfect-forwarded via std::forward
。但是我的客户端代码最多只能使用 3 个参数成功调用它。任何更多的东西都无法编译。出了什么问题以及如何解决?现在有没有加入许多范围的Boost实体?
我已经复制并粘贴了该 OP 的实现,在此处对其进行编辑只是为了提高空白可读性并添加相关的 headers:
#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
包的长度为 1,因此生成的 join(std::forward<Arg0>(arg0))
扩展使用第一个重载, 在范围内是 。
如果有四个或更多参数,生成的 join(std::forward<Arg0>(arg0), ..., std::forward<ArgN>(argN))
扩展需要第二次重载,但它不在 selfsame 的后期 return 类型的范围内。
解决这个问题的一种方法是将函数模板集转换为成员函数模板集,因为 class 范围更宽松:
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 {};
请注意,重要的是 class 范围,而不是我们选择使用 operator()
作为成员函数模板的名称。您也可以使用名为 foo
的静态成员函数模板,并在转发给它的 class 之外有一个普通函数模板。
现在我们可以揭露第二个问题,因为实现有问题并且只能处理奇数个参数!即使 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 {};