从 C++ 中的单个向量创建成对的向量

Create a vector of pairs from a single vector in C++

我有一个大小均匀的向量,我想将其转换成对向量,其中每对向量始终包含两个元素。我知道我可以使用简单的循环来做到这一点,但我想知道是否有一个很好的标准库工具可以做到这一点?可以假设原始向量总是包含偶数个元素。

示例:

vector<int> origin {1, 2, 3, 4, 5, 6, 7, 8};

vector<pair<int, int>> goal { {1, 2}, {3, 4}, {5, 6}, {7, 8} };

直观但不幸的是无效的方法

有一种quick-and-dirty方法,它会kinda-hopefully-maybe做你要求的,甚至根本不会复制数据...但缺点是你不能确定它会起作用。它依赖于 undefined behavior,因此不推荐使用。我描述它是因为我相信这是人们凭直觉想象的,我们可以做到的。

所以,它是关于使用 和矢量数据的 re-interpretation:

std::vector<int> origin {1, 2, 3, 4, 5, 6, 7, 8};
auto raw_data = reinterpret_cast<std::pair<int, int>*>(origin.data());
std::span<std::pair<int, int>> goal { raw_data, origin.size()/2 };

GodBolt

上查看

注意事项:

  • reinterpret_cast 是“脏的”。它正式导致未定义的行为,它在实践中的作用取决于编译器和平台。如果您也忘记了 std::pair,而是使用二维 mdspan,则可以避免这种情况。当然,mdspan 不在标准库中。
  • 这是 C++20。在 C++20 之前,您仍然可以使用 span,但它们不在标准库中。
  • 具体来说,如果您在不允许未对齐访问的平台上,坚持在 goal.data() 处存在 twice-the-size-of-int 值可能是个问题。

使用 Range-v3:

#include <range/v3/range/conversion.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/chunk.hpp>

using namespace ranges;
using namespace ranges::views;

int main() {
    std::vector<int> origin {1, 2, 3, 4, 5, 6, 7, 8};
    std::vector<std::pair<int, int>> goal { {1, 2}, {3, 4}, {5, 6}, {7, 8} };

    auto constexpr makePairFromRangeOf2 = [](auto two){
        return std::make_pair(two.front(), two.back());
    };

    auto result = origin | chunk(2)
                         | transform(makePairFromRangeOf2)
                         | to_vector;
}

请注意,如果您只需要在 result 上循环,则可以省略 | to_vector,因为您仍然可以执行 result.begin()result.end() ,例如。

如果您不需要内部容器真正成为 std::pairs,但您只是喜欢调用 result.front().front() 而不是 result.front().first,那么您可以离开还有 transform,对 auto result = origin | chunk(2); 感到满意。

你没有提到为什么你只想要一个标准的解决方案。但是请考虑 <ranges> 是 C++20 中的 标准。不幸的是,该功能不如 pre-C++20 Range-v3 库强大。但它会在某个时候(C++23?),我认为毫无疑问。

我有一个函数可以处理向量中的偶数和奇数元素。它的作用是使用另一个参数在对的末尾添加一个数字。 我认为从 C++ 20 开始没有任何标准 tool/library 可以这样做,Range-v3 库将在 C++ 23 尚未发布。

这里是try it onlinelink.

#include <iostream>
#include <vector>

// time complexity: O(n / 2), where `n` is the length of `my_vec`
std::vector<std::pair<int, int>> vec_to_pair(const std::vector<int> &my_vec, int odd_origin)
{
    std::vector<std::pair<int, int>> val;
    for (std::size_t i = 0; i < my_vec.size(); i += 2)
    {
        int sec_val;
        if (i < my_vec.size() - 1)
            sec_val = my_vec[i + 1];
        else if (my_vec.size() % 2 != 0)
            sec_val = odd_origin;
        else 
            break;
        int data[] = {my_vec[i], sec_val};
        val.push_back({data[0], data[1]});
    }
    return val;
}

void print(const std::vector<std::pair<int, int>> &vec)
{
    std::cout << "{ ";
    for (auto &&i : vec)
        std::cout << "{ " << i.first << ", " << i.second << " }  ";
    std::cout << " }" << std::endl;
}

int main(void)
{
    std::vector<int> vec1 = {1, 2, 3, 4, 5};    // odd
    std::vector<int> vec2 = {1, 2, 3, 4, 5, 6}; // even

    auto x1 = vec_to_pair(vec1, -1);
    auto x2 = vec_to_pair(vec2, 0);

    print(x1);
    print(x2);

    return 0;
}

如@康桐薇所述,如果您还愿意使用 ranges-v3 库,则可以使用 chunk() 视图:

std::vector origin = {1, 2, 3, 4, 5, 6, 7, 8};
auto goal = v | ranges::views::chunk(2) | ranges::to<std::vector>;

看到它在 GodBolt.

上运行

与我的 不同,这将是完全有效的 language-wise。

注意事项:

  • 这将复制您的数据!
  • 可能会在您的目标代码中引入 一堆 内容(错误字符串、异常处理程序等)。
  • 范围库显着增加了编译时间(尽管启用 C++20 后编译时间可能会减少?)
  • 不基于标准库 - 但显然 chunk()to() 将在 C++23 中,因此最多进行轻微的语法调整(例如添加 std::),这将是有效的 C++23,仅包含标准库。
  • goal 的元素不是 std::pair,而是范围。您将需要获取第一个和第二个,或第一个和最后一个元素来组成实际的一对。