展开 std::vector 进入参数包
Expand std::vector into parameter pack
我有具有以下签名的方法:
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 的方式正确吗?如果是,能否请您提供一个简单的示例,说明如何将其应用到我的问题中?
这是不可能的,模板方法调用是在编译时绑定的,但是 std::vector
直到运行时才知道它包含多少项,因此无法混合这两个概念。
DoStuff(vElemets...);
这里编译器应该根据vElements
有多少个元素来选择正确的实现。您会看到这种想法的缺陷,因为 std::vector
只是一个对象,在调用时可以包含任意数量的项目。
问题是,从 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_sequence
和 std::index_sequence
仅从 C++14 开始可用。在 C++11 中,您必须以某种方式替换它们。
这是可能的,只要您提供参数数量的上限。
对 C++11 使用 std::index_sequence
的 Xeo's implementation:
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{});
}
我有具有以下签名的方法:
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 的方式正确吗?如果是,能否请您提供一个简单的示例,说明如何将其应用到我的问题中?
这是不可能的,模板方法调用是在编译时绑定的,但是 std::vector
直到运行时才知道它包含多少项,因此无法混合这两个概念。
DoStuff(vElemets...);
这里编译器应该根据vElements
有多少个元素来选择正确的实现。您会看到这种想法的缺陷,因为 std::vector
只是一个对象,在调用时可以包含任意数量的项目。
问题是,从 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_sequence
和 std::index_sequence
仅从 C++14 开始可用。在 C++11 中,您必须以某种方式替换它们。
这是可能的,只要您提供参数数量的上限。
对 C++11 使用 std::index_sequence
的 Xeo's implementation:
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{});
}