Boost:: range:: join для нескольких диапазонов
Я хочу сделать следующее:
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)...));
}
Ответы
Ответ 1
Есть две перегрузки boost:: join
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;
}
}
Демо-версия.
Я не рассматривал ваши вариационные функции, но проблема, вероятно, схожа.