【问题标题】:Expand std::vector into parameter pack将 std::vector 展开为参数包
【发布时间】:2019-10-30 21:31:48
【问题描述】:

我有以下签名的方法:

void DoStuff(int i);
void DoStuff(int i, k);
void DoStuff(int i, int k, int l);

我有一个方法,我想调用 DoStuff 方法,如下所示:

void CallDoStuff(const std::vector<int>& vElements) {
  // What magic is supposed to happen here to make vElements an expandable pack?
  DoStuff(vElemets...);
}

有没有机会做到这一点? 以正确的方式使用 std::index_sequence 吗?如果是,您能否提供一个简单的示例,如何将其应用于我的问题?

【问题讨论】:

    标签: c++11 variadic-templates stdvector index-sequence


    【解决方案1】:

    这是可能的,只要您提供参数数量的上限。

    在 C++11 中使用 Xeo's implementationstd::index_sequence

    template <unsigned... Idx>
    void trampoline(const std::vector<int>& vElements, seq<Idx...>) {
        return DoStuff(vElements[Idx]...);
    }
    
    template <std::size_t Arity>
    void trampoline(const std::vector<int>& vElements) {
        return trampoline(vElements, typename gen_seq<Arity>::seq{});
    }
    
    template <unsigned... Idx>
    void CallDoStuff(const std::vector<int>& vElements, seq<Idx...>) {
        using trampoline_t = void (*)(const std::vector<int>&);
        constexpr trampoline_t trampolines[]{
            trampoline<Idx>...
        };
        trampolines[vElements.size()](vElements);
    }
    
    template <std::size_t Max>
    void CallDoStuff(const std::vector<int>& vElements) {
        assert(vElements.size() <= Max);
        return CallDoStuff(vElements, typename gen_seq<Max + 1>::seq{});
    }
    

    See it live on Wandbox

    【讨论】:

    • 这个答案最能解决我的问题,因为我可以包装函数调用 CallDoStuff(vElements)。但所有其他答案也是正确的。
    【解决方案2】:

    问题在于,从 std::vector 中,您不能 -- compile time -- 提取 size() 值。

    因此,只有将要从向量中使用的元素数量作为编译时已知值传递给CallDoStuff(),您才能获得所需的内容。

    例如,您可以将其作为模板值传递。

    使用辅助函数,您可以编写如下内容

    template <std::size_t ... Is>
    void CallDoStuff (std::vector<int> const & vElements,
                      std::index_sequence<Is...> const &)
     { DoStuff(vElements[Is]...); }
    
    template <std::size_t N>
    void CallDoStuff (std::vector<int> const & vElements)
     { CallDoStuff(vElements, std::make_index_sequence<N>{}); }
    

    电话可能是这样的

    CallDoStuff<5u>(v);
    

    如果你可以使用std::array,而不是std::vector,答案就不同了:你可以从类型本身中提取size(),所以

    template <std::size_t N, std::size_t ... Is>
    void CallDoStuff (std::array<int, N> const & vElements,
                      std::index_sequence<Is...> const &)
     { DoStuff(vElements[Is]...); }
    
    template <std::size_t N>
    void CallDoStuff (std::array<int, N> const & vElements)
     { CallDoStuff(vElements, std::make_index_sequence<N>{}); }
    

    无需解释 N 即可调用,如下所示

    std::array<int, 5u>  arr { 2, 3, 5, 7, 11 };
    
    CallDoStuff(arr); // no more <5u>
    

    尾注:注意 std::make_index_sequencestd::index_sequence 仅从 C++14 开始可用。在 C++11 中,您必须以某种方式替换它们。

    【讨论】:

    • 我完全同意你的观点,模板需要在编译时知道大小,而向量的大小只有在运行时才知道。
    • 如果你使用 std::array 那么 std::apply 是一个简单的解决方案。
    • @JakeDenham-Dyson - 你的意思是std::apply(DoStuff, arr);?不幸的是,在这种情况下,它的工作方式并不简单:DoStuff() 是一个重载函数,因此编译器无法选择正确的版本。而且,std::apply()是在C++17中引入的,问题被标记为C++11(但这也是我的答案是C++14的问题)。
    • 有人可以为菜鸟分解一下吗
    【解决方案3】:

    这无法做到,模板方法调用在编译时绑定,但 std::vector 直到运行时才知道它包含多少项,因此无法混合这两个概念。

    DoStuff(vElemets...);
    

    这里编译器应该根据vElements有多少元素来选择正确的实现。您会看到这种想法的缺陷,因为 std::vector 只是一个在调用点可以包含任意数量的项目的对象。

    【讨论】:

    • 正如已经回复 max66,我完全同意你的观点,Jack,模板需要在 compile-time 知道大小,而向量的大小只在 运行时.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-03
    • 1970-01-01
    相关资源
    最近更新 更多