i.s*_*tav 15 c++ templates variadic-templates
Suppose I have
template<int ...>
struct Ints { };
class MyClass
{
public:
Ints<1, 2, 3> get() { return Ints<1, 2, 3>(); }
};
Run Code Online (Sandbox Code Playgroud)
What I want to do is simple.
template <class T>
vector<int> MyFunc1(T& x)
{
Ints<S...> result = x.get();
vector<int> t = { S... };
return t;
}
Run Code Online (Sandbox Code Playgroud)
Somewhat like this. (Here MyClass can be one example of T.) Apparently, for compiler S... seems to invalid.
template <class T, int... S>
vector<int> MyFunc2(T& x)
{
Ints<S...> result = x.get();
vector<int> t = { S... };
return t;
}
Run Code Online (Sandbox Code Playgroud)
This doesn't work either. I think from get() the S... becomes specific and automatically deducible, but compiler doesn't recognize it. (I'm not sure but C++ doesn't deduce template parameters looking inside the function, but only arguments and return type)
The only way I've found is to use another function that finds out what int... was.
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);
}
Run Code Online (Sandbox Code Playgroud)
It works well, but requires another extra helper function that does nothing but just provides syntactically clear way to match the S... using templates.
我真的只想在单个函数中执行此操作。是否真的每次都想检索参数包时是否必须定义辅助功能?
编辑:Ints,MyFunc这只是玩具的例子。我想知道检索模板参数的一般方法!
如果给定类型的变量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 + ...); };
Run Code Online (Sandbox Code Playgroud)
这是一种简单的统一语法,使我们可以将S其用作参数包。
这部分也很简单。我们接受一个type变量Ints<S...>和一个函数,并使用S...。
template<int... S, class Func>
auto operator|(Ints<S...>, Func&& f) {
return f(S...);
}
Run Code Online (Sandbox Code Playgroud)
如前所述,静态案例与动态案例具有相似的界面,并且从概念上讲不会太多。从用户的角度来看,唯一的区别是S...,我们使用ll useS.value ...作为包而不是用作参数包。
对于每个值,我们希望将其封装在以值为模板的相应类型中。这使我们可以在constexpr上下文中访问它。
template<int Value>
struct ConstInt {
constexpr static int value = Value;
};
Run Code Online (Sandbox Code Playgroud)
为了与动态情况区分开来,我将重载/而不是|。否则,它们的行为类似。除了将值包装在ConstInt类中,并且每个值都有其自己的类型之外,该实现与动态情况几乎相同。
template<int... S, class F>
auto operator/(Ints<S...>, F&& func) {
return func(ConstInt<S>()...);
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
因此,扩展参数包非常简单明了,并且最终与动态情况几乎相同,唯一的不同之处.value在于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 + ...); };
Run Code Online (Sandbox Code Playgroud)
那么有什么新消息?由于value是constexpr,S.value因此可以用作模板参数。在此示例中,我们使用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)...);
};
Run Code Online (Sandbox Code Playgroud)
在此示例中,我们将序列中的每个元素平方,然后返回一个新序列:
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)...>();
};
Run Code Online (Sandbox Code Playgroud)
如果要避免运算符重载,我们可以从函数式编程中获得一些启发,并使用unpack函数来处理事情,如下所示:
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>()...); };
}
Run Code Online (Sandbox Code Playgroud)
那是什么unpack?该函数采用一堆值,然后返回一个函数,该函数采用另一个函数并将该函数以vals作为输入。
该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
Run Code Online (Sandbox Code Playgroud)
我们可以使用以下代码重新编写示例apply_ints:
// 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 + ...); });
Run Code Online (Sandbox Code Playgroud)
本附录简要概述了如何更一般地使用此语法(例如,在使用多个单独的参数包时)。
为了让您更好地了解此接口的灵活性,下面是一个示例,其中我们使用它来配对来自两个单独包的值。
Ints<1, 2, 3> intsA;
Ints<10, 20, 30> intsB;
// pairs = {{1, 10}, {2, 20}, {3, 30}}
auto pairs = intsA | [&](auto... S1) {
return intsB | [&](auto... S2) {
return std::vector{ std::pair{S1, S2}... };
};
};
Run Code Online (Sandbox Code Playgroud)
注意: MSVC和GCC都可以毫无问题地编译此示例,但是c不休。我认为MSVC和GCC是正确的,但我不确定。
这个示例稍微复杂一点,但是我们还可以创建二维值数组,这些二维数组从单独包装中的所有值组合中提取。
在这种情况下,我用它来创建时间表。
Ints<1, 2, 3, 4, 5, 6, 7, 8, 9> digits;
auto multiply = [](auto mul, auto... vals) {
return std::vector{(mul * vals)...};
};
auto times_table = digits | [&](auto... S1) {
return digits | [&](auto... S2) {
return std::vector{ multiply(S1, S2...)... };
};
};
Run Code Online (Sandbox Code Playgroud)
在C ++ 2a中,您可以使用模板化的lambda在函数内部定义您的助手,例如:
auto v = []<std::size_t...Is>(std::index_sequence<Is...>){return std::vector{Is...};}(seq);
// ^^^^^^^^^^^^^^^^^^ New in C++2a
Run Code Online (Sandbox Code Playgroud)