几乎按顺序连接两个 C++ std::vectors [已关闭]

Virtually sequentially concatenate two C++ std::vectors [closed]

提问人:Damir Tenishev 提问时间:4/10/2023 最后编辑:Damir Tenishev 更新时间:4/12/2023 访问量:183

问:


想改进这个问题吗?通过编辑这篇文章添加详细信息并澄清问题。

8个月前关闭。

这篇文章是在 8 个月前编辑并提交审核的,但未能重新打开帖子:

原始关闭原因未解决

目标是在函数调用时以虚拟方式(这意味着不应发生真正的串联)按顺序连接不同类型的对象的两个 C++ std::vector。

我在不同的向量中有一些类的对象,并希望一些函数将它们作为一个整体进行处理。我不想使用虚函数,动态内存分配既不想将所有不同类型的对象(指向它们的指针)放在同一个容器中,因为在大多数代码中它们都以不同的方式处理,我不会支付额外的价格,也不会为我的类制作复杂的接口。

以下是草稿:

#include <vector>
#include <iostream>

struct A {
  void foo() { std::cout << "Hey, I'm A!\n"; }
};

struct B {
  void foo() { std::cout << "Hey, I'm B!\n"; }
};

void bar(???) {
   ???.foo();
}

int main()  {
    std::vector<A> va(2);
    std::vector<B> vb(3);

    bar(???);
    return 0;
}

当然,我可以逐个处理所有这些向量,但我有很多向量,这会导致代码重复,并在添加新类型时忘记更新此类代码的风险。

更新后来我发现,我真的不需要函数调用时刻的串联,而是动态连接向量的别名,下面接受的答案涵盖了两者。

C++ OOP 模板 C++-标准库

评论

2赞 Alan Birtles 4/10/2023
在任何人能够给你答案之前,你需要提供更多关于你要做的事情的细节。一个最小的可重现的例子是你尝试过什么,以及你遇到的问题可能会有所帮助
1赞 JaMiT 4/10/2023
“sequentially concatenate” -- 有没有其他方法可以连接?
0赞 Damir Tenishev 4/12/2023
@JaMiT,这是一个措辞问题,但我的意图是强调,我不想像某些库中的zip函数(thrust,std::ranges::views::zip等)那样将SoA(数组结构)转换为AoS(结构数组)。

答:

3赞 SRNissen 4/10/2023 #1

目标是在函数调用时以虚拟方式(这意味着不应发生真正的串联)按顺序连接两个不同类型的对象的 C++。std::vector

我的理解是,这是不可行的,因为在编译时不可能生成将处理不同类型的对象(在函数中)而不使用动态多态性的代码。

如果所有类型在编译时都是已知的,则可以使用静态多态性来完成 - 当取消引用时,连接视图将返回的不是基础元素,而是包含指向基础类型的指针的对象。std::variant

不过,它不会很优雅。

但是 - 看到您的示例 - 您是否考虑过模板函数方法?如果两者都有方法,你可以ABfoo

template<typename Vec>
void do_foo(Vec const & vec)
{
    for(auto & element : vec)
        element.foo()
}

do_foo(va);
do_foo(vb);

现在,如果出现新需求,您无需担心忘记更新其中一个向量的函数,因为它们都是从同一模板中标记的。

评论

0赞 Damir Tenishev 4/10/2023
感谢您的输入。尽管 Evg 的答案更全面,但您的回答帮助我理解我答错了问题。我不能“现在不担心忘记更新函数”,因为我有这个 do_foo(va) 等列表,就我而言,我将有许多数组和许多地方我必须解决这个问题。现在我从 Evg 的答案中看到了如何解决这个问题,我将元组存储为别名。
4赞 Evg 4/10/2023 #2

您可以使用可变参数包折叠表达式进行一些元编程。例如:

template<class... Ts>
void bar(Ts&... vecs) {
    const auto call_foo_for_each = [](auto& vec) {
        for (auto& v : vec)
            v.foo();
    };
    (call_foo_for_each(vecs), ...);
}

void caller()  {
    bar(va, vb /*, ... */);
}

演示

如果你想把它们作为一个参数传递,你可以把它们打包成一个元组(引用的):

template<class... Ts>
void bar(std::tuple<Ts...> tuple) {
    const auto call_foo_for_each = [](auto& vec ) {
        for (auto& v : vec)
            v.foo();
    };
    std::apply([&](auto&... vecs) {
        (call_foo_for_each(vecs), ...);
    }, tuple);
}

void caller()  {
    bar(std::tie(va, vb /*, ... */));
}

评论

1赞 Evg 4/10/2023
@DamirTenishev 我认为您不能使用标准库缩短此代码。不幸的是,在元编程方面,标准库并没有那么丰富。
1赞 Evg 4/10/2023
@DamirTenishev 即使有了 Boost,我也想不出更短的解决方案。
1赞 Evg 4/10/2023
@DamirTenishev 从文字描述来看,很难说。尝试实现,看看它是否有效。这种方法的主要限制是,在编译时应该知道放入元组的向量。也就是说,您不能在运行时对元组执行类似“push_back”的操作,因为该操作会更改元组类型,而C++不允许这样做。
1赞 Evg 4/10/2023
@DamirTenishev 您的代码是正确的。通常,在使用元组之前,请确保元组所包含的引用不会失效。
1赞 Evg 4/12/2023
@DamirTenishev 很遗憾,如果它是数据成员,则不能使用扣除。.std::tuplestd::tuple<std::vector<A>&, std::vector<B>&> all_objects = std::tie(va, vb);