Этот код работает и генерирует правильный вывод (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
который не может?
Ну, это работает, но это также заставляет меня создавать это array_of_size
заранее, чтобы я мог передать его в качестве второго аргумента, тогда как было бы полезнее, если бы это было возвращаемое значение, чтобы я мог напрямую передавать его другим функциям. если мне нужно. Однако ваше предложение укрепляет мое понимание того, что мое решение не работает, потому что размер вывода transformed
не известен во время компиляции (даже если это может быть, потому что размер входного массива известен во время компиляции).
Так мой комментарий отвечает на ваш вопрос? Могу ли я превратить его в фактический ответ?
@NutCracker, ну, вы, конечно, можете, и я поставлю +1, но прежде чем принять, я хотел бы знать, почему copy_range
не работает и есть ли решения «ввод-вывод», а не решения «мутированный ввод».
Если вы скомпилируете с -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()>{});
}
А как насчет
boost::range::copy
? Вы тоже пробовали? Проблема в том, что элементамиarray_of_vectors
являютсяstd::vector
s. Попробуйте сделать элементыarray_of_vectors
std::array
s.