假设我有
template<int ...>
struct Ints { };
class MyClass
{
public:
Ints<1, 2, 3> get() { return Ints<1, 2, 3>(); }
};
我想做的很简单。
template <class T>
vector<int> MyFunc1(T& x)
{
Ints<S...> result = x.get();
vector<int> t = { S... };
return t;
}
有点像这样。 (这里MyClass
可能是T
的一个示例。)显然,对于编译器S...
似乎无效。
template <class T, int... S>
vector<int> MyFunc2(T& x)
{
Ints<S...> result = x.get();
vector<int> t = { S... };
return t;
}
这也不起作用。我认为get()
从S...
开始变得特定并且可以自动推论,但是编译器无法识别。 (我不确定,但C ++不会推断出在函数内部查找的模板参数,而只能推断出参数和返回类型)
我发现的唯一方法是使用另一个找出int...
是什么的函数。
template <int ...S>
vector<int> temp(Ints<S...> not_used)
{
return { S... };
}
template <class T>
vector<int> MyFunc3(T& x)
{
auto result = x.get();
return temp(result);
}
它运作良好,但需要另一个额外的辅助函数,该函数什么也不做,只是提供了语法上清晰的方式来使用模板匹配S...
。
我真的想仅通过单个功能来执行此操作。我真的真的必须每次都想检索参数包时定义辅助功能吗?
编辑:Ints
和MyFunc
只是玩具示例。我想知道检索模板参数的一般方法!
答案 0 :(得分:7)
如果给定类型为Ints<S...>
的变量,则理想情况下我们可以使用S...
并进行尽可能少的修改。
在这种情况下,我们可以设计一个接口,该接口允许我们将参数包用作可变参数或lambda的输入,甚至可以将这些值用作模板参数。
静态案例和动态案例都有相似的接口,但是动态案例更简洁一些,因此可以更好地进行介绍。给定变量和函数,我们将函数与变量定义中包含的参数包一起应用。
Ints<1, 2, 3> ints;
// Get a vector from ints
// vec = {1, 2, 3}
auto vec = ints | [](auto... S) { return std::vector {S...}; };
// Get an array from ints
// arr = {1, 2, 3}
auto arr = ints | [](auto... S) { return std::array {S...}; };
// Get a tuple from ints
// tup = {1, 2, 3}
auto tup = ints | [](auto... S) { return std::make_tuple(S...); };
// Get sum of ints using a fold expression
auto sum = ints | [](auto... S) { return (S + ...); };
这是一种简单的统一语法,使我们可以使用S
并将其用作参数包。
这部分也很简单。我们使用类型为Ints<S...>
的变量和一个函数,并使用S...
来应用该函数。
template<int... S, class Func>
auto operator|(Ints<S...>, Func&& f) {
return f(S...);
}
如前所述,静态案例与动态案例具有相似的界面,并且从概念上讲不会太多。从用户的角度来看,唯一的区别是我们S...
S.value ...`作为参数包而不是使用ll use
作为参数包。
对于每个值,我们希望将其封装在以值为模板的相应类型中。这使我们可以在constexpr上下文中访问它。
template<int Value>
struct ConstInt {
constexpr static int value = Value;
};
为了将其与动态情况区分开来,我将重载/
而不是|
。否则,它们的行为类似。除了将值包装在ConstInt
类中,并且每个值都有其自己的类型之外,该实现与动态情况几乎相同。
template<int... S, class F>
auto operator/(Ints<S...>, F&& func) {
return func(ConstInt<S>()...);
}
C ++允许我们使用与非静态成员相同的语法访问类的静态成员,而不会失去constexpr
状态。
假设我有一些ConstInt
,其值为10。我可以直接使用I.value
作为模板参数,也可以使用decltype(I)::value
:
// This is what'll be passed in as a parameter
ConstInt<10> I;
std::array<int, I.value> arr1;
std::array<int, decltype(I)::value> arr2;
// Both have length 10
因此,扩展参数包非常简单,并且最终与动态情况几乎相同,唯一的区别是附加到.value
的{{1}}。下面显示的是动态案例的示例,这次使用静态案例语法:
S
那么有什么新消息? 由于Ints<1, 2, 3> ints;
// Get a vector from ints
auto vec = ints | [](auto... S) { return std::vector {S.value...}; };
// Get an array from ints
// arr = {1, 2, 3}
auto arr = ints | [](auto... S) { return std::array {S.value...}; };
// Get a tuple from ints
auto tup = ints | [](auto... S) { return std::make_tuple(S.value...); };
// Get sum of ints using a fold expression
auto sum = ints | [](auto... S) { return (S.value + ...); };
是constexpr,value
可以用作模板参数。在本示例中,我们使用S.value
使用{{1 }}:
S.value
在此示例中,我们将序列中的每个元素平方,然后返回一个新序列:
std::get
如果要避免运算符重载,我们可以从函数式编程中汲取灵感,并使用auto tupA = std::make_tuple(10.0, "Hello", 3);
auto indicies = Ints<2, 0, 1>{};
// tupB = {3, 10.0, "Hello"}
auto tupB = indicies / [&](auto... S) {
return std::make_tuple(std::get<S.value>(tupA)...);
};
函数处理事物,其编写方式如下:
auto ints = Ints<0, 1, 2, 3, 4, 5>();
// ints_squared = Ints<0, 1, 4, 9, 16, 25>();
auto ints_squared = ints / [](auto... S) {
return Ints<(S.value * S.value)...>();
};
unpack
是什么??此函数采用一堆值,并返回一个函数,该函数采用另一个函数并将该函数与vals一起用作输入。
通过template<int... vals>
auto unpack(Ints<vals...>) {
return [](auto&& f) { return f(vals...); };
}
// Static case
template<int... vals>
auto unpack_static(Ints<vals...>) {
return [](auto&& f) { return f(ConstInt<vals>()...); };
}
函数,我们可以将这些值作为参数应用到其他函数。
我们可以将结果分配给名为unpack
的变量,然后可以使用unpack
处理所有特定用例:
apply_ints
我们可以使用apply_ints
重新编写以前的示例:
Ints<1, 2, 3> ints; //this variable has our ints
auto apply_ints = unpack(ints); // We use this function to unpack them
本附录简要概述了如何更广泛地使用此语法(例如,当使用多个单独的参数包时)。
为使您更好地了解此接口的灵活性,在下面的示例中,我们使用它来配对来自两个单独包装的值。
apply_ints
注意::MSVC和GCC都可以毫无问题地编译此示例,但是c不休。我认为MSVC和GCC是正确的,但我不确定。
此示例稍微复杂一点,但是我们还可以创建值的二维数组,这些数组从单独包中的所有值组合中提取。
在这种情况下,我用它来创建时间表。
// Get a vector from ints
// vec = {1, 2, 3}
auto vec = apply_ints([](auto... S) { return std::vector {S...}; });
// Get an array from ints
// arr = {1, 2, 3}
auto arr = apply_ints([](auto... S) { return std::array {S...}; });
// Get a tuple from ints
// tup = {1, 2, 3}
auto tup = apply_ints([](auto... S) { return std::make_tuple(S...); });
// Get sum of ints using a fold expression
auto sum = apply_ints([](auto... S) { return (S + ...); });
答案 1 :(得分:6)
在C ++ 2a中,您可以使用模板化的lambda在函数内定义您的助手,例如:
auto v = []<std::size_t...Is>(std::index_sequence<Is...>){return std::vector{Is...};}(seq);
// ^^^^^^^^^^^^^^^^^^ New in C++2a
答案 2 :(得分:1)
我建议向struct Int
添加函数以获取不同的表示形式
#include <vector>
#include <array>
template<int ...values>
struct Ints {
auto getAsVector() const {
return std::vector<int>({ values... });
}
constexpr auto getAsArray() const {
return std::array<int, sizeof...(values)>({ values... });
}
};
class MyClass
{
public:
Ints<1, 2, 3> get() { return Ints<1, 2, 3>(); }
};
int main() {
MyClass a;
auto array = a.get().getAsVector();
return array.size();
}
答案 3 :(得分:1)
如果您不使用/创建帮助模板,则需要其他方式来提供值。
我想到的最简单,规范和通用的方法是将它们放在相同的类范围内,以便您的Ints
结构变为:
template<int ...ints>
struct Ints {
constexpr static std::initializer_list<int> vals = {ints...};
};
由于它是constexpr
,因此应在编译时进行评估,并且不会产生运行时成本。
现在您将可以执行以下操作:
return std::vector<int>(ints.vals);