展开具有不同长度的参数包
Expand parameter packs with different lengths
我想"生成"一个函数指针的跳转表。所指向的函数是用两种类型模板化的。在两个类型列表中,每个可能的对都应该有一个不同的函数。理想情况下,我们可以有这样的东西:
#include <tuple>
template <typename X, typename Y>
void foo()
{}
template <typename... Xs, typename... Ys>
void bar(const std::tuple<Xs...>&, const std::tuple<Ys...>&)
{
using fun_ptr_type = void (*) (void);
static constexpr fun_ptr_type jump_table[sizeof...(Xs) * sizeof...(Ys)]
= {&foo<Xs, Ys>...};
}
int main ()
{
using tuple0 = std::tuple<int, char, double>;
using tuple1 = std::tuple<float, unsigned long>;
bar(tuple0{}, tuple1{});
}
正如预期的那样,当元组具有不同的长度时,它会失败:
foo.cc:15:20: error: pack expansion contains parameter packs 'Xs' and 'Ys' that have different lengths (3 vs. 2)
= {&foo<Xs, Ys>...};
~~ ~~ ^
foo.cc:23:3: note: in instantiation of function template specialization 'bar<int, char, double, float, unsigned long>' requested here
bar(tuple0{}, tuple1{});
^
1 error generated.
为了实现这种功能,我已经尝试并成功地使用了间接方法(第一个跳转表,其中包含指向具有另一个跳转表的函数的指针),但我发现它很笨拙。
所以,我的问题是:有解决办法吗?
您的示例代码是错误的,即使是在编译的情况下(即sizeof…(Xs)==sizeof。。。(Y))。比方说,您有N元元组,那么jump_table有N*N个元素,但只有前N个元素是用ptrs函数初始化的。
首先,您需要内部加入两个类型列表:
template<class A, class B>
struct P;
template<class... Ts>
struct L {};
template<class T, class... Ts>
using mul = L<P<T, Ts>...>;
template<class...>
struct cat;
template<class T>
struct cat<T>
{
using type = T;
};
template<class... As, class... Bs>
struct cat<L<As...>, L<Bs...>>
{
using type = L<As..., Bs...>;
};
template<class A, class B, class... Ts>
struct cat<A, B, Ts...>
{
using type = typename cat<typename cat<A, B>::type, Ts...>::type;
};
template<class A, class B>
struct join;
template<class... As, class... Bs>
struct join<L<As...>, L<Bs...>>
{
using type = typename cat<mul<As, Bs...>...>::type;
};
例如
join<L<int[1], int[2]>, L<float[1], float[2], float[3]>>::type
给你
L<P<int[1], float[1]>, P<int[1], float[2]>, P<int[1], float[3]>, P<int[2], float[1]>, P<int[2], float[2]>, P<int[2], float[3]>
回到你的例子:
template <typename X, typename Y>
void foo()
{}
template<class T, std::size_t N>
struct jump_table
{
template<class... As, class... Bs>
constexpr jump_table(L<P<As, Bs>...>)
: table{&foo<As, Bs>...}
{}
T table[N];
};
template <typename... Xs, typename... Ys>
void bar(const std::tuple<Xs...>&, const std::tuple<Ys...>&)
{
using fun_ptr_type = void (*) (void);
static constexpr jump_table<fun_ptr_type, sizeof...(Xs) * sizeof...(Ys)> table
= {typename join<L<Xs...>, L<Ys...>>::type()};
}
int main ()
{
using tuple0 = std::tuple<int, char, double>;
using tuple1 = std::tuple<float, unsigned long>;
bar(tuple0{}, tuple1{});
}
这应该会达到你的预期。
对于手头的问题来说,这里的其他答案似乎太复杂了。以下是我的做法:
#include <array>
#include <tuple>
template <typename X, typename Y> void foo() {}
using fun_ptr_type = void (*) (void);
// Build one level of the table.
template <typename X, typename ...Ys>
constexpr std::array<fun_ptr_type, sizeof...(Ys)>
jump_table_inner = {{&foo<X, Ys>...}};
// Type doesn't matter, we're just declaring a primary template that we're
// about to partially specialize.
template <typename X, typename Y> void *jump_table;
// Build the complete table.
template <typename ...Xs, typename ...Ys>
constexpr std::array<std::array<fun_ptr_type, sizeof...(Ys)>, sizeof...(Xs)>
jump_table<std::tuple<Xs...>, std::tuple<Ys...>> = {jump_table_inner<Xs, Ys...>...};
int main () {
using tuple0 = std::tuple<int, char, double>;
using tuple1 = std::tuple<float, unsigned long>;
// Call function for (int, float).
jump_table<tuple0, tuple1>[0][0]();
}
Clang 3.5在其C++14模式中接受了这一点。
产品扩展context( f<Xs, Ys>... ) /* not what we want */
的正常解决方案是将其重写为context2( g<Xs, Ys...>... )
。意味着g
负责针对某些X
扩展Ys
,并且最终扩展针对所有Xs
执行g
。这种重写的结果是我们引入了额外的嵌套,从而引入了不同的上下文。
在我们的例子中,我们将有一个函数指针数组数组,而不是函数指针的平面数组与您尝试的解决方案不同,尽管这些确实是我们关心的&foo<X, Y>
函数指针,而且平坦化很简单。
#include <cassert>
#include <utility>
#include <array>
template<typename X, typename Y>
void foo() {}
using foo_type = void(*)();
template<typename... T>
struct list {
static constexpr auto size = sizeof...(T);
};
template<typename X, typename Y, typename Indices = std::make_index_sequence<X::size * Y::size>>
struct dispatch;
template<
template<typename...> class XList, typename... Xs
, template<typename...> class YList, typename... Ys
, std::size_t... Indices
>
struct dispatch<XList<Xs...>, YList<Ys...>, std::index_sequence<Indices...>> {
private:
static constexpr auto stride = sizeof...(Ys);
using inner_type = std::array<foo_type, stride>;
using multi_type = inner_type[sizeof...(Xs)];
template<typename X, typename... Yss>
static constexpr inner_type inner()
{ return {{ &foo<X, Yss>... }}; }
static constexpr multi_type multi_value = {
inner<Xs, Ys...>()...
};
public:
static constexpr auto size = sizeof...(Xs) * sizeof...(Ys);
static constexpr foo_type value[size] = {
multi_value[Indices / stride][Indices % stride]...
};
};
template<
template<typename...> class XList, typename... Xs
, template<typename...> class YList, typename... Ys
, std::size_t... Indices
>
constexpr foo_type dispatch<XList<Xs...>, YList<Ys...>, std::index_sequence<Indices...>>::value[size];
int main()
{
using dispatch_t = dispatch<
list<int, char, double>,
list<float, unsigned long>
>;
constexpr auto&& table = dispatch_t::value;
static_assert( dispatch_t::size == 6, "" );
static_assert( table[0] == &foo<int, float>, "" );
static_assert( table[1] == &foo<int, unsigned long>, "" );
static_assert( table[2] == &foo<char, float>, "" );
static_assert( table[3] == &foo<char, unsigned long>, "" );
static_assert( table[4] == &foo<double, float>, "" );
static_assert( table[5] == &foo<double, unsigned long>, "" );
}
Coliru演示。
实际上,您拥有的更像是两个列表(<X1,Y1>
、<X2,Y2>
…)的"zip",当列表的长度不同时,它就不起作用了。
要计算两者的"乘积",我认为必须使用辅助类才能使其工作。看看其他类似你的问题:如何创建类型列表的笛卡尔乘积?
- 如何反转整数参数包
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用默认参数等选择模板专业化
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 函数调用中参数的顺序重要吗
- 部分定义/别名模板模板参数
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- 使用不带参数的函数访问结构元素
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 如何使用Luacneneneba API正确读取字符串和表参数
- 在派生函数中指定void*参数
- 视图中的参数推导失败:take_while
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 使用指向成员的指针将成员函数作为参数传递
- 没有名称的C++模板参数
- 如何将enable-if与模板参数和参数包一起使用