【问题标题】:Can I use boost::copy_range on the result of boost::adaptors::transformed over std::array to get back another std::array?我可以对 boost::adaptors::transformed over std::array 的结果使用 boost::copy_range 来取回另一个 std::array 吗?
【发布时间】:2021-03-21 16:55:11
【问题描述】:

此代码有效并生成正确的输出(23,因为23array_of_vectors 中两个std::vector<int>s 的大小:

#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;
    }
}

另一方面,如果我尝试通过更改这个来强制通过boost::copy_range 强制array_of_sizes 确实是std::array&lt;std::size_t, 2u&gt;

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&lt;std::size_t&gt;,例如

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

有效!看起来transformedstd::array&lt;T,N&gt; 转换为可转换为std::vector&lt;T&gt; 的范围,就像它丢失了std::array 的编译时大小一样。

这个错误/行为是由什么引起的?

我只能认为由于std::array 的编译时已知大小而不是std::vector 的运行时大小,某些东西不起作用。可能transformed 无法处理?或者可能是copy_range 不能?

【问题讨论】:

  • boost::range::copy 怎么样?你也试过了吗?问题是array_of_vectors 的元素是std::vectors。尝试制作array_of_vectors的元素std::arrays。
  • 嗯,它确实有效,但它也迫使我事先创建 array_of_size 以便我可以将它作为第二个参数传递,而如果它是一个返回值会更有用,这样我可以在需要时直接将其传递给其他函数。但是,您的建议加强了我的理解,即我的解决方案不起作用,因为在编译时不知道transformed 的输出大小(即使可以,因为输入数组的大小 is 在编译时知道)。
  • 我的评论能回答你的问题吗?我可以把它变成实际的答案吗?
  • @NutCracker,你当然可以,我会 +1,但在接受之前我想知道 为什么 copy_range 不起作用,如果有“输入-输出”解决方案,而不是“变异输入”解决方案。

标签: c++ arrays templates boost c++17


【解决方案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 的 lambda,您可以编写一个替代函数,该函数使用简单的元编程来生成该元素列表:

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()>{});
}

Example.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-16
    • 2013-08-23
    • 2016-05-12
    • 1970-01-01
    • 2015-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多