Существует ли модифицируемая версия view range :: view :: transform? - PullRequest
3 голосов
/ 22 апреля 2019

Рассмотрим следующую программу:

#include <iostream>
#include <algorithm>
#include <numeric>
#include <array>
#include <range/v3/view/transform.hpp>

int main() {
    using container = std::array<std::tuple<int,float,double>, 4>;
    container tuples {{
        {1, 4.f, 8.},
        {2, 5.f, 9.},
        {3, 6.f, 10.},
        {4, 7.f, 11.}
    }};

    auto int_view =
        tuples | ranges::view::transform( [](auto& t){return std::get<int>(t);} );

    // int_view[1] = 3; // (*)

    auto x = std::accumulate(int_view.begin(), int_view.end(), 0);
    std::cout << "x = " << x << std::endl;
}

Компилирует и печатает 10;но - если я раскомментирую строку (*) - она ​​не скомпилируется, так как GCC жалуется, что левая сторона равенства не является lvalue.Я был отчасти разочарован этим - я надеялся, что преобразование произведет int&, которые я мог бы назначить ...

Есть ли что-то, что я могу сделать, чтобы это изменяемое представление?Или какой-то другой механизм в библиотеке диапазонов, который позволил бы мне эквивалент модифицируемого представления?

1 Ответ

3 голосов
/ 22 апреля 2019

Проблема с вашим кодом очень проста, если подумать:

Функция преобразования на самом деле является функцией проекции, и ваша не дает ссылок, необходимых для разрешения изменения источника, потому что стандартное правило вывода типа возврата для лямбда-выражений использует правила для простого auto, и те, которые никогда не выводят ссылки.

  1. Одно исправление - это изменение правил вычета для decltype(auto), которые сохраняют ссылки, и поэтому их лучше избегать, если вы не знаете, они правы.

    auto int_view = tuples | ranges::view::transform(
        [](auto& t)->decltype(auto){return std::get<int>(t);});
    
  2. Или вы можете явно запросить ссылки с auto& или что-то более конкретное.

    auto int_view = tuples | ranges::view::transform(
        [](auto& t)->auto&{return std::get<int>(t);});
    
  3. И, наконец, никто не мешает вам вернуть прокси, как std::reference_wrapper. Хотя это ненужное осложнение.

    auto int_view = tuples | ranges::view::transform(
        [](auto& t){return std::ref(std::get<int>(t));});
    
...