Существует ли изменяемая версия ranges ::view::transform для просмотра?

#c #range-v3

#c #диапазон-v3

Вопрос:

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

 #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( [](autoamp; 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 . Я был немного разочарован этим — я надеялся, что преобразование приведет к созданию intamp; ‘s, которые я мог бы назначить…

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

Ответ №1:

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

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

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

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

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

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