Могу ли я использовать boost::copy_range для результата boost::adaptors::transformed поверх std::array, чтобы получить другой std::array?

Этот код работает и генерирует правильный вывод (23, поскольку 2 и 3 — это размеры двух std::vector<int> в array_of_vectors:

#include <array>
#include <boost/range/adaptor/transformed.hpp>
#include <iostream>
#include <vector>

using boost::adaptors::transformed;

constexpr auto get_size = [](auto const& snip){ return snip.size(); };

int main() {
    std::array<std::vector<int>,2> array_of_vectors = {
        {std::vector<int>{1,1}, std::vector<int>{1,1,1}}
    };

    auto array_of_sizes = array_of_vectors | transformed(get_size);
    for (auto i : array_of_sizes) {
        std::cout << i;
    }
}

С другой стороны, если я попытаюсь установить, что array_of_sizes действительно является std::array<std::size_t, 2u> через boost::copy_range, изменив это

auto array_of_sizes = array_of_vectors | transformed(get_size);

к этому

auto array_of_sizes = boost::copy_range<std::array<std::size_t, 2u>>(
        array_of_vectors | transformed(get_size)
        );

Я получаю следующую ошибку,

$ g++ -std=c++17 deleteme.cpp && ./a.out 
In file included from /usr/include/boost/range/iterator_range.hpp:13,
                 from /usr/include/boost/range/adaptor/transformed.hpp:16,
                 from deleteme.cpp:2:
/usr/include/boost/range/iterator_range_core.hpp: In instantiation of ‘SeqT boost::copy_range(const Range&) [with SeqT = std::array<long unsigned int, 2>; Range = boost::range_detail::transformed_range<<lambda(const auto:1&)
 2> >]’:
deleteme.cpp:16:114:   required from here
/usr/include/boost/range/iterator_range_core.hpp:842:20: error: no matching function for call to ‘std::array<long unsigned int, 2>::array(boost::range_detail::extract_const_iterator<boost::range_detail::transformed_range<<la
y<std::vector<int>, 2> >, true>::type, boost::range_detail::extract_const_iterator<boost::range_detail::transformed_range<<lambda(const auto:1&)>, std::array<std::vector<int>, 2> >, true>::type)’
  842 |             return SeqT( boost::begin( r ), boost::end( r ) );
      |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from deleteme.cpp:1:
/usr/include/c++/10.2.0/array:94:12: note: candidate: ‘std::array<long unsigned int, 2>::array()’
   94 |     struct array
      |            ^~~~~
/usr/include/c++/10.2.0/array:94:12: note:   candidate expects 0 arguments, 2 provided
/usr/include/c++/10.2.0/array:94:12: note: candidate: ‘constexpr std::array<long unsigned int, 2>::array(const std::array<long unsigned int, 2>&)’
/usr/include/c++/10.2.0/array:94:12: note:   candidate expects 1 argument, 2 provided
/usr/include/c++/10.2.0/array:94:12: note: candidate: ‘constexpr std::array<long unsigned int, 2>::array(std::array<long unsigned int, 2>&&)’
/usr/include/c++/10.2.0/array:94:12: note:   candidate expects 1 argument, 2 provided

С другой стороны, как ни удивительно для меня (!), если я заставлю array_of_sizes быть std::vector<std::size_t>, например.

auto array_of_sizes = boost::copy_range<std::vector<std::size_t>>( // vector instead of array
        array_of_vectors | transformed(get_size)
        );

оно работает! Похоже, что transformed превращает std::array<T,N> в диапазон, конвертируемый в std::vector<T>, точно так же, как теряется размер std::array во время компиляции.

С чем связана эта ошибка/поведение?

Я могу только думать, что что-то не работает из-за известного размера std::array во время компиляции, в отличие от размера во время выполнения std::vector. Наверное transformed не может с этим справиться? Или, может быть, это copy_range который не может?

А как насчет boost::range::copy? Вы тоже пробовали? Проблема в том, что элементами array_of_vectors являются std::vectors. Попробуйте сделать элементы array_of_vectorsstd::arrays.

NutCracker 10.12.2020 10:29

Ну, это работает, но это также заставляет меня создавать это array_of_size заранее, чтобы я мог передать его в качестве второго аргумента, тогда как было бы полезнее, если бы это было возвращаемое значение, чтобы я мог напрямую передавать его другим функциям. если мне нужно. Однако ваше предложение укрепляет мое понимание того, что мое решение не работает, потому что размер вывода transformed не известен во время компиляции (даже если это может быть, потому что размер входного массива известен во время компиляции).

Enlico 10.12.2020 10:39

Так мой комментарий отвечает на ваш вопрос? Могу ли я превратить его в фактический ответ?

NutCracker 10.12.2020 10:40

@NutCracker, ну, вы, конечно, можете, и я поставлю +1, но прежде чем принять, я хотел бы знать, почему copy_range не работает и есть ли решения «ввод-вывод», а не решения «мутированный ввод».

Enlico 10.12.2020 10:44
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
568
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вы скомпилируете с -std=c++2a, gcc 10.2 будет иметь хорошее сообщение об ошибке:

boost_1_74_0/boost/range/iterator_range_core.hpp:842:38: error: array must be initialized with a brace-enclosed initializer
  842 |             return SeqT( boost::begin( r ), boost::end( r ) );
      |                          ~~~~~~~~~~~~^~~~~

Проблема в том, что boost::copy_range знает только, как создавать контейнеры, которые принимают пару итераторов в качестве (одного из) параметров своего конструктора, поэтому он несовместим с std::array, у которого на самом деле нет никаких конструкторов — его можно построить только как агрегат из (скрепленного скобками ) список элементов.

С лямбда-выражениями C++20 вы можете написать альтернативную функцию, которая использует простое метапрограммирование для создания этого списка элементов:

template<class T, class R>
T copy_range_fixed_size(R const& r) {
    return [&r]<std::size_t... I>(std::index_sequence<I...>) {
        auto it = boost::begin(r);
        return T{(I, *it++)...};
    }(std::make_index_sequence<T{}.size()>{});
}

Пример.

Другие вопросы по теме