[英]How to join multiple boost ranges and return as a result from function w/o using boost::any_range
[英]boost::range::join for multiple ranges
我想做以下事情:
std::vector<int> a = {1,2,3}, b = {4,5,6}, c = {7,8,9};
for(auto&& i : join(a,b,c)) {
i += 1
std::cout << i; // -> 2345678910
}
我嘗試使用boost::range::join
,效果很好:
auto r = boost::join(a,b);
for(auto&& i : boost::join(r,c)) {
i += 1;
std::cout << i; // -> 2345678910
}
鏈接連接,讀取操作工作:
for(auto&& i : boost::join(boost::join(a,b),c))
std::cout << i; // -> 123456789
但是,寫作不起作用:
for(auto&& i : boost::join(boost::join(a,b),c)) {
i += 1; // Fails :(
std::cout << i;
}
我的可變連接有同樣的問題,即適用於閱讀但不適用於寫作:
template<class C> C&& join(C&& c) { return c; }
template<class C, class D, class... Args>
auto join(C&& c, D&& d, Args&&... args)
-> decltype(boost::join(boost::join(std::forward<C>(c), std::forward<D>(d)),
join(std::forward<Args>(args)...))) {
return boost::join(boost::join(std::forward<C>(c), std::forward<D>(d)),
join(std::forward<Args>(args)...));
}
Mehrdad 在評論中給出了解決方案
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)...));
}
template<typename SinglePassRange1, typename SinglePassRange2>
joined_range<const SinglePassRange1, const SinglePassRange2>
join(const SinglePassRange1& rng1, const SinglePassRange2& rng2)
template<typename SinglePassRange1, typename SinglePassRange2>
joined_range<SinglePassRange1, SinglePassRange2>
join(SinglePassRange1& rng1, SinglePassRange2& rng2);
當你這樣做時
for(auto&& i : boost::join(boost::join(a,b), c)) {
// ^^^^ ^^^^ temporary here
// ||
// calls the const ref overload
您會獲得一個臨時的joined_range
並且由於它們只能綁定到const 引用,因此選擇了第一個重載,它返回一個不允許修改的范圍。
如果您避免臨時工作,您可以解決這個問題:
#include <boost/range.hpp>
#include <boost/range/join.hpp>
int main()
{
std::vector<int> a = {1,2,3}, b = {4,5,6}, c = {7,8,9};
auto range = boost::join(a,b);
for(int& i : boost::join(range,c)) {
i += 1;
std::cout << i;
}
}
我沒有研究過你的可變參數函數,但問題可能是相似的。
這是一個完整的解決方案,可以在 GCC 12 上正常工作。對於 GCC 10 和 11, subranges
function 可用於獲取子范圍數組,然后可用作| std::views::join
的 lhs 參數 | std::views::join
。
編輯:這些函數只返回具有共同迭代器類型的范圍。 如果您沒有通用的迭代器類型,一個選擇是從范圍創建一個新容器(這可能不是您想要的),或者創建一個具有不同子范圍的自定義類型(不能使用與std::views::join
)。
#include <ranges>
#include <vector>
#include <iostream>
#include <tuple>
#include <array>
#include <algorithm>
namespace detail {
template<std::size_t N, typename... Ts>
struct has_common_type_helper {
using T1 = std::decay_t<std::tuple_element_t<N-1, std::tuple<Ts...>>>;
using T2 = std::decay_t<std::tuple_element_t<N-2, std::tuple<Ts...>>>;
static constexpr bool value = std::same_as<T1, T2> && has_common_type_helper<N-1, Ts...>::value;
};
template<typename... Ts>
struct has_common_type_helper<0, Ts...> : std::false_type {
static_assert(std::is_void_v<Ts...>, "Undefined for an empty parameter pack");
};
template<typename... Ts>
struct has_common_type_helper<1, Ts...> : std::true_type {};
template<typename T> struct iterator_types;
template<std::ranges::range... Ts>
struct iterator_types<std::tuple<Ts...>> {
using type = std::tuple<std::ranges::iterator_t<Ts>...>;
};
}
template<typename T>
struct has_common_type;
template<typename T1, typename T2>
struct has_common_type<std::pair<T1,T2>> {
static constexpr bool value = std::same_as<std::decay_t<T1>, std::decay_t<T2>>;
};
template <typename... Ts>
struct has_common_type<std::tuple<Ts...>> : detail::has_common_type_helper<sizeof...(Ts), Ts...> {};
template <typename T>
inline constexpr bool has_common_type_v = has_common_type<T>::value;
template<std::size_t I = 0, typename Array, typename... Ts, typename Func> requires (I == sizeof...(Ts))
void init_array_from_tuple(Array& a, const std::tuple<Ts...>& t, Func fn)
{
}
template<std::size_t I = 0, typename Array, typename... Ts, typename Func> requires (I < sizeof...(Ts))
void init_array_from_tuple(Array& a, const std::tuple<Ts...>& t, Func fn)
{
a[I] = fn(std::get<I>(t));
init_array_from_tuple<I+1>(a, t, fn);
}
template<std::ranges::range... Ranges>
auto subranges(Ranges&&... rngs)
{
using IteratorTypes = detail::iterator_types<std::tuple<Ranges...>>::type;
static_assert(has_common_type_v<IteratorTypes>);
using SubrangeT = std::ranges::subrange<std::tuple_element_t<0, IteratorTypes>>;
auto subrngs = std::array<SubrangeT, sizeof...(Ranges)>{};
auto t = std::tuple<Ranges&&...>{std::forward<Ranges>(rngs)...};
auto fn = [](auto&& rng) {
return std::ranges::subrange{rng.begin(), rng.end()};
};
init_array_from_tuple(subrngs, t, fn);
return subrngs;
}
#if __GNUC__ >= 12
template<std::ranges::range... Ranges>
auto join(Ranges&&... rngs)
{
return std::ranges::owning_view{subranges(std::forward<Ranges>(rngs)...) | std::views::join};
}
#endif
int main()
{
std::vector<int> v1{1,2,3};
std::vector<int> v2{4};
std::vector<int> v3{5,6};
#if __GNUC__ >= 12
std::ranges::copy(join(v1,v2,v3,v1), std::ostream_iterator<int>(std::cout, " "));
#else
auto subrngs = subranges(v1,v2,v3,v1);
std::ranges::copy(subrngs | std::views::join, std::ostream_iterator<int>(std::cout, " "));
#endif
std::cout << '\n';
return 0;
}
這是一個適用於具有公共引用類型的兩個不同范圍的實現。 您可以使用蠻力方法將其擴展到 3 個范圍。
#include <ranges>
#include <vector>
#include <list>
#include <iostream>
#include <algorithm>
template<std::ranges::range Range1, std::ranges::range Range2>
auto join2(Range1&& rng1, Range2&& rng2)
{
using Ref1 = std::ranges::range_reference_t<Range1>;
using Ref2 = std::ranges::range_reference_t<Range2>;
using Ref = std::common_reference_t<Ref1, Ref2>;
class Iter {
public:
using value_type = std::remove_cv_t<std::remove_reference_t<Ref>>;
using difference_type = std::ptrdiff_t;
Iter() = default;
Iter(Range1&& rng1_, Range2&& rng2_, bool begin)
: m_it1{begin ? rng1_.begin() : rng1_.end()}
, m_it2{begin ? rng2_.begin() : rng2_.end()}
, m_e1{rng1_.end()} {}
bool operator==(const Iter& rhs) const {
return m_it1 == rhs.m_it1 && m_it2 == rhs.m_it2;
}
Ref operator*() const {
return m_it1 != m_e1 ? *m_it1 : *m_it2;
}
Iter& operator++() {
(m_it1 != m_e1) ? (void)++m_it1 : (void)++m_it2;
return *this;
}
Iter operator++(int) {
Iter ret = *this;
++(*this);
return ret;
}
private:
std::ranges::iterator_t<Range1> m_it1;
std::ranges::iterator_t<Range2> m_it2;
std::ranges::iterator_t<Range1> m_e1;
};
static_assert(std::forward_iterator<Iter>);
auto b = Iter{std::forward<Range1>(rng1), std::forward<Range2>(rng2), true};
auto e = Iter{std::forward<Range1>(rng1), std::forward<Range2>(rng2), false};
return std::ranges::subrange<Iter>{b, e};
}
int main()
{
std::vector<int> v{1,2,3};
std::list<int> l{4,5,6};
std::ranges::copy(join2(v,l), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
return 0;
}
PS 我對可變參數的實現並不樂觀,盡管我確信比我聰明的人能夠弄清楚。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.