vector<int> GetData()
{
return { 1,2,3,4,5,6,7,8,9 };
}
auto doubled = GetData() | boost::adaptors::transformed([](auto& i) {return i * 2; });
С этим диапазоном сделать мы ничего не сможем, т.к. адаптер будет применяться к временному объекту (коряво, но идея, думаю, понятна). А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?
Оператор | перегружен для адаптора и рэнджа и возвращает рэндж. Рэндж по определению не является владельцем данных, что-то вроде view (так, кстати, это называется в новых Ranges Эрика Неблера). Так что нужно провернуть все действия с адапторами, пока временный объект не разрушился, значит, нужно писать oneliner.
df>С этим диапазоном сделать мы ничего не сможем, т.к. адаптер будет применяться к временному объекту (коряво, но идея, думаю, понятна). А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?
не очень понятна идея, но есть такие варианты:
1) переопределить оператор присваивания — но тогда забыть про auto
2) добавить через вертикальную строку преобразователь в нужный формат (типа как в LINQ для проекции)
3) использовать подобие расширяющих методов в с++: расширяющие методы (вот тут p-strade/oven что-то япошки пилили, но сложно сказать в каком оно сейчас состоянии)
Здравствуйте, df, Вы писали:
df>С этим диапазоном сделать мы ничего не сможем, т.к. адаптер будет применяться к временному объекту (коряво, но идея, думаю, понятна). А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?
Сохранить результат в другой контейнер с нужным временем жизни?
Здравствуйте, df, Вы писали:
df>А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?
Придётся либо использовать только в рамках выражения (в котором живёт временная переменная), либо сохранять. Можно сделать обёртку, которая внутри будет хранить и исходный контейнер, и адаптеры (либо уже адаптированный range), и сама обёртка при этом будет является range. То есть примерно:
auto doubled = make_wrapper_owner(GetData(), transformed(_1 * 2));
df>>С этим диапазоном сделать мы ничего не сможем, т.к. адаптер будет применяться к временному объекту (коряво, но идея, думаю, понятна). А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?
A>Сохранить результат в другой контейнер с нужным временем жизни?
A>
A>vector<int> GetData()
A>{
A> return { 1,2,3,4,5,6,7,8,9 };
A>}
A>std::vector<int> doubled;
A>boost::push_back(doubled, GetData() | boost::adaptors::transformed([](auto& i) {return i * 2; }));
A>
в итоге так и сделал. Городить ничего не стал. Смысла нет. Просто подумал вдруг это уже как-то решено (типа обертка какая есть), а я не знаю.
df>>А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?
EP>Придётся либо использовать только в рамках выражения (в котором живёт временная переменная), либо сохранять. Можно сделать обёртку, которая внутри будет хранить и исходный контейнер, и адаптеры (либо уже адаптированный range), и сама обёртка при этом будет является range. То есть примерно: EP>
Думал мало-ли есть уже готовое что-то подобное. Но, наверное, не очень актуально это.
ПС: а без лямбды гораздо лаконичнее смотрится, спасибо. А то по привычке стал вставлять ее везде.
Здравствуйте, df, Вы писали:
df>Думал мало-ли есть уже готовое что-то подобное. Но, наверное, не очень актуально это.
Не встречал. Момент не приятный, да.
df>ПС: а без лямбды гораздо лаконичнее смотрится, спасибо. А то по привычке стал вставлять ее везде.
Это самый лаконичный вариант, лаконичнее наверно не придумаешь (чуть короче можно через карринг — * 2, но это менее читаемо, ИМХО).
df>ПС: а без лямбды гораздо лаконичнее смотрится, спасибо.
Но время компиляции с Boost.Phoenix (либо Boost.Lambda) намного больше.
Кстати, товарищ jazzer показывал трюк
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Так весь же смысл transformed именно в ленивости. Энергично можно и через std/boost::transform/range-based-for посчитать.
Ну где-то ж ленивость должна закончиться? У ТС она очевидно должна закончиться, пока живет вектор, который он вернул из GetData(), т.е. до ;. Ничего с этим не поделаешь
Здравствуйте, andyp, Вы писали:
EP>>Так весь же смысл transformed именно в ленивости. Энергично можно и через std/boost::transform/range-based-for посчитать. A>Ну где-то ж ленивость должна закончиться? У ТС она очевидно должна закончиться, пока живет вектор, который он вернул из GetData(), т.е. до ;. Ничего с этим не поделаешь
Необязательно. Можно сделать обёртку, которая переместит вектор и адаптеры во внутрь себя, и при этом сама будет являться range, схематично:
template< ..., typename LazyRange>
class wrapper
{
vector<...> inner_vector;
LazyRange lazy_range;
public:
...
auto begin() const ...;
auto end() const ...;
};
Вектор будет жить столько, сколько будет жить обёртка — её можно даже вернуть вверх по стэку.
Если совсем не оптимально, то можно ещё вот так проиллюстрировать:
auto shared_data = make_shared<vector<int>>( GetData() );
auto doubled = *shared_data | boost::adaptors::transformed([shared_data](auto i){ return i * 2; });
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Необязательно. Можно сделать обёртку, которая переместит вектор и адаптеры во внутрь себя, и при этом сама будет являться range, схематично:
Вектор будет жить столько, сколько будет жить обёртка — её можно даже вернуть вверх по стэку. EP>Если совсем не оптимально, то можно ещё вот так проиллюстрировать:
A>Все так, но судя по всему (тут вангую) ТС именно этого и хотел избежать. Можно ж было просто
A>
A>const std::vector<int>& data = GetData();
A>//тут строим нужный пайплайн из вьюшек
A>
A>сделать.
Именно так.
Причем хотелось _бы_ именно _готовое_ решение. Потому как понятно, что _руками_ красоту можно навести. Т.е. вполне можно было бы написать такой враппер:
auto r = GetData() | holder | boost::adaptors::transformed([](auto& i) {return i * 2; })
выглядит нормально имхо. Но суть от этого не меняется (причем в идеале holder должен уметь отличать rvalue от lvalue).
Как мне кажется, делать такое _самому_ лишено большого смысла. А вот если бы было готовое в рамках того же буста, к примеру, то почему бы не воспользоваться.
Здравствуйте, df, Вы писали:
df>Именно так. df>Причем хотелось _бы_ именно _готовое_ решение. Потому как понятно, что _руками_ красоту можно навести. Т.е. вполне можно было бы написать такой враппер:
df>
df>auto r = GetData() | holder | boost::adaptors::transformed([](auto& i) {return i * 2; })
df>
df>выглядит нормально имхо. Но суть от этого не меняется (причем в идеале holder должен уметь отличать rvalue от lvalue). df>Как мне кажется, делать такое _самому_ лишено большого смысла. А вот если бы было готовое в рамках того же буста, к примеру, то почему бы не воспользоваться.
На сколько понял, по факту тебе требуется нечто типа позднего связывания range и последовательности, чтобы иметь возможность определить алгоритм обработки элементов диапазона заранее, а затем в месте вызова связать его с контейнером или другим range. Не уверен, что требуемый адаптер существует в boost. Т.е. получить поведение типа:
auto r = make_unbound_range() | boost::adaptors::transformed([](auto& i) {return i * 2; });
std::vector<int> doubled;
boost::push_back(doubled, r | bind_range(getData()));
Можно ли это прикрутить малой кровью, я не знаю Может Евгений что скажет.
df>>Причем хотелось _бы_ именно _готовое_ решение. Потому как понятно, что _руками_ красоту можно навести. Т.е. вполне можно было бы написать такой враппер: df>>
df>>auto r = GetData() | holder | boost::adaptors::transformed([](auto& i) {return i * 2; })
df>>
df>>если бы было готовое в рамках того же буста, к примеру, то почему бы не воспользоваться.
A>На сколько понял, по факту тебе требуется нечто типа позднего связывания range и последовательности, чтобы иметь возможность определить алгоритм обработки элементов диапазона заранее, а затем в месте вызова связать его с контейнером или другим range. Не уверен, что требуемый адаптер существует в boost.
Скорее применение адаптеров к rvalue.
Основной смысл вопроса был в том существует-ли что-либо готовое. Нет, понятно.
А все остальное, повторюсь, "овчинка выделки не стоит". Ну или академический интерес (тоже хорошо).
Вы правильно сказали, писать враппер только для того, чтобы скрыть в нем холдер (вектор)... не уверен.
A>
A>auto r = make_unbound_range() | boost::adaptors::transformed([](auto& i) {return i * 2; });
A>std::vector<int> doubled;
A>boost::push_back(doubled, r | bind_range(getData()));
A>
A>Можно ли это прикрутить малой кровью, я не знаю Может Евгений что скажет.