用于转换多个参数的模板函数
Template function for casting multiple number of params
我正在尝试编写一个模板函数,它将根据其模板参数执行一组 dynamic_casts()
。我有以下说明:
class FooData
{
public:
virtual ~FooData() {};
};
class DerivedFooData: public FooData{};
class Other: public FooData{};
void bar(DerivedFooData* d1, DerivedFooData* d2) {}
void bar(DerivedFooData* d1, Other*, DerivedFooData* d2, Other*, Other*) {}
int main()
{
DerivedFooData d1,d2;
std::vector<FooData*> container1{&d1, &d2};
std::vector<FooData*> container2{&d1, &d2};
// I want bar to be called with container1[0] cast to DerivedFooData and container2[1] cast to DerivedFooData
// the first two template params are each container size
foo<1, 1, DerivedFooData, DerivedFooData>(container, container2);
// I want bar to be called with
// container1[0] cast to DerivedFooData
// container1[1] cast to Other
// container2[0] cast to DerivedFooData
// container2[1] cast to Other
// container2[2] cast to Other
foo<2, 3, DerivedFooData, Other, DerivedFooData, Other, Other>(container, container2);
}
我可以手动创建其中一些:
template <int N, int M, typename U, typename V>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
bar(dynamic_cast<U*>(input[0]), dynamic_cast<V*>(output[0]));
}
template <int N, int M, typename U, typename V, typename W, typename X, typename Y>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
bar(dynamic_cast<U*>(input[0]), dynamic_cast<V*>(input[1]), dynamic_cast<W*>(output[0]), dynamic_cast<X*>(output[1]), dynamic_cast<Y*>(output[2]));
}
但我不知道如何以通用方式指定 N
和 M
的所有组合。我假设可变参数模板会出现在某个地方,但我需要一些指导。
任何模板递归背后的基本思想都是一次处理一个参数,在函数上递归并删除一个输入类型参数,然后在模板参数列表为空时终止。
处理两个可变类型列表的一种常见方法是定义一个 "pack" 您可以专门研究的类型,其中包采用可变数量的模板参数。这使您能够轻松分离多组可变类型参数。
在这里,我通过声明 type_pack
没有任何实现来演示这个例子(这很好,我们只将它用作一个类型,我们从不实例化它)然后我声明 [=12= 的多个特化] 旨在:
- 从两个列表中剥离第一个类型并执行
dynamic_cast
。
- 通过删除相关包类型的第一个类型参数来确定下一步。
- 当第一个包变空时过渡到第二个包。
- 将处理(转换)的参数传递到下一步。
- 最后,当两个包都为空时,使用计算出的参数调用
bar()
。
template <typename...>
struct type_pack;
// Base declaration that we will specialize.
template <int, int, typename, typename>
struct foo_fn;
// Specialization handling when the first pack is not empty.
template <int OffsetA, int OffsetB, typename THead, typename... T1, typename... T2>
struct foo_fn<OffsetA, OffsetB, type_pack<THead, T1...>, type_pack<T2...>> {
template <typename... Args>
static void f(foovec_t const & input, foovec_t const & output, Args && ... args) {
return foo_fn<
OffsetA + 1,
OffsetB,
type_pack<T1...>,
type_pack<T2...>
>::f(input, output, std::forward<Args>(args)..., dynamic_cast<THead *>(input[OffsetA]));
}
};
// Specialization handling when the first pack is empty and the second
// pack is not empty.
template <int OffsetA, int OffsetB, typename THead, typename... T>
struct foo_fn<OffsetA, OffsetB, type_pack<>, type_pack<THead, T...>> {
template <typename... Args>
static void f(foovec_t const & input, foovec_t const & output, Args && ... args) {
return foo_fn<
OffsetA,
OffsetB + 1,
type_pack<>,
type_pack<T...>
>::f(input, output, std::forward<Args>(args)..., dynamic_cast<THead *>(output[OffsetB]));
}
};
// Specialization handling the terminating case (all packs empty).
template <int OffsetA, int OffsetB>
struct foo_fn<OffsetA, OffsetB, type_pack<>, type_pack<>> {
template <typename... Args>
static void f(foovec_t const &, foovec_t const &, Args && ... args) {
bar(std::forward<Args>(args)...);
}
};
// Helper type to provide the two initial integer values.
template <typename, typename>
struct foo;
template <typename... T1, typename... T2>
struct foo<type_pack<T1...>, type_pack<T2...>> {
static void f(foovec_t const & input, foovec_t const & output) {
foo_fn<0, 0, type_pack<T1...>, type_pack<T2...>>::f(input, output);
}
};
您可以在第二个示例中将其称为 foo<type_pack<DerivedFooData, Other>, type_pack<DerivedFooData, Other, Other>>::f(container, container2)
。请注意,您不必提供任何尺寸;这些是根据每包的大小推断的。
参见 this demo 并注意类型不匹配的指针参数作为空值出现。
我不会尝试定义 bar()
,因为我假设您已经这样做了,或者知道如何去做。我示例中的 bar()
只接受特定的指针类型(为了测试转换是否正确执行)。
此代码仅使用 C++11 功能。
请注意,std::forward
不是绝对必要的,因为转换值始终是指针。但是,在转发可变大小的参数列表时养成使用它的习惯是很好的。如果值很大 strings/vectors 那么每一步转发都会消除大量无用的复制。
一点也不优雅(并且不确定确切的索引)但是......下面的东西(假设你可以使用 C++14)应该可以工作(如果我理解正确的话你想要什么)
template <std::size_t Dim1, typename ... Ts, std::size_t ... Is>
void foo_helper (std::index_sequence<Is...>, std::vector<FooData*> inV,
std::vector<FooData*> outV)
{ bar( dynamic_cast<Ts*>(Is < Dim1 ? inV[Is] : outV[Is-Dim1])... ); }
template <std::size_t Dim1, std::size_t Dim2, typename ... Ts>
void foo (std::vector<FooData*> inV, std::vector<FooData*> outV)
{ foo_helper<Dim1, Ts...>
(std::make_index_sequence<Dim1+Dim2>{}, inV, outV); }
我知道 C++20 对您来说太新了,但是为了好玩,我将向您展示如何使用新的 C++20 lambda 模板功能来避免使用辅助函数
// from C++20: foo_helper() isn't needed anymore
template <std::size_t Dim1, std::size_t Dim2, typename ... Ts>
void foo (std::vector<FooData*> inV, std::vector<FooData*> outV)
{ [&]<std::size_t ... Is>(std::index_sequence<Is...>)
{ bar( dynamic_cast<Ts*>(Is < Dim1 ? inV[Is] : outV[Is-Dim1])... ); }
(std::make_index_sequence<Dim1+Dim2>{}); }
下面是一个完整的编译C++14的例子
#include <vector>
#include <type_traits>
struct FooData { virtual ~FooData() {}; };
class DerivedFooData: public FooData { };
class Other : public FooData { };
void bar (DerivedFooData*, DerivedFooData*) {}
void bar (DerivedFooData*, Other*, DerivedFooData*, Other*, Other*) {}
template <std::size_t Dim1, typename ... Ts, std::size_t ... Is>
void foo_helper (std::index_sequence<Is...>, std::vector<FooData*> inV,
std::vector<FooData*> outV)
{ bar( dynamic_cast<Ts*>(Is < Dim1 ? inV[Is] : outV[Is-Dim1])... ); }
template <std::size_t Dim1, std::size_t Dim2, typename ... Ts>
void foo (std::vector<FooData*> inV, std::vector<FooData*> outV)
{ foo_helper<Dim1, Ts...>
(std::make_index_sequence<Dim1+Dim2>{}, inV, outV); }
int main ()
{
DerivedFooData d1, d2, d3;
std::vector<FooData*> container1 {&d1, &d2};
std::vector<FooData*> container2 {&d1, &d2, &d3};
foo<1, 1, DerivedFooData, DerivedFooData>(container1, container2);
foo<2, 3, DerivedFooData, Other, DerivedFooData, Other, Other>
(container1, container2);
}
我更喜欢 "pack type" 语法而不是给出额外的数字,所以
foo<std::tuple<DerivedFooData, Other>, std::tuple<DerivedFooData, Other, Other>>
而不是你的:
foo<2, 3, DerivedFooData, Other, DerivedFooData, Other, Other>
您仍然可以:
template <typename Tuple, std::size_t ... Is1, std::size_t ... Is2, typename ... Ts>
void foo(std::index_sequence<Is1...>, std::index_sequence<Is2...>, Ts&&...args)
{
foo<std::tuple<std::tuple_element_t<Is1, Tuple>...>,
std::tuple<std::tuple_element_t<sizeof...(Is1) + Is2, Tuple>...>>(
std::forward<Ts>(args)...);
}
template <std::size_t N1, std::size_t N2, typename ... Ts>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
static_assert(N1 + N2 == sizeof...(Ts), "!");
foo<std::tuple<Ts...>>(std::make_index_sequence<N1>{},
std::make_index_sequence<N2>{},
input,
output);
}
使用您的语法。
现在使用主助手将每个矢量元素转换为 tuple
template <typename Pack> struct dynamic_cast_as_tuple;
template <typename ...Ts>
struct dynamic_cast_as_tuple<std::tuple<Ts...>>
{
template <typename T>
std::tuple<Ts*...> operator ()(const std::vector<T*>& v) const
{
return (*this)(v, std::index_sequence_for<Ts...>{});
}
private:
template <typename T, std::size_t ... Is>
std::tuple<Ts*...> operator ()(const std::vector<T*>& v, std::index_sequence<Is...>) const
{
return {dynamic_cast<Ts*>(v[Is])...};
}
};
然后,想要的功能是:
template <typename pack1, typename pack2>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
std::apply([](auto*... ps){ bar(ps...); },
std::tuple_cat(
dynamic_cast_as_tuple<pack1>{}(input),
dynamic_cast_as_tuple<pack2>{}(output))
);
}
std::index_sequence
是C++14和std::apply
c++17,但可以在C++11中实现。
我正在尝试编写一个模板函数,它将根据其模板参数执行一组 dynamic_casts()
。我有以下说明:
class FooData
{
public:
virtual ~FooData() {};
};
class DerivedFooData: public FooData{};
class Other: public FooData{};
void bar(DerivedFooData* d1, DerivedFooData* d2) {}
void bar(DerivedFooData* d1, Other*, DerivedFooData* d2, Other*, Other*) {}
int main()
{
DerivedFooData d1,d2;
std::vector<FooData*> container1{&d1, &d2};
std::vector<FooData*> container2{&d1, &d2};
// I want bar to be called with container1[0] cast to DerivedFooData and container2[1] cast to DerivedFooData
// the first two template params are each container size
foo<1, 1, DerivedFooData, DerivedFooData>(container, container2);
// I want bar to be called with
// container1[0] cast to DerivedFooData
// container1[1] cast to Other
// container2[0] cast to DerivedFooData
// container2[1] cast to Other
// container2[2] cast to Other
foo<2, 3, DerivedFooData, Other, DerivedFooData, Other, Other>(container, container2);
}
我可以手动创建其中一些:
template <int N, int M, typename U, typename V>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
bar(dynamic_cast<U*>(input[0]), dynamic_cast<V*>(output[0]));
}
template <int N, int M, typename U, typename V, typename W, typename X, typename Y>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
bar(dynamic_cast<U*>(input[0]), dynamic_cast<V*>(input[1]), dynamic_cast<W*>(output[0]), dynamic_cast<X*>(output[1]), dynamic_cast<Y*>(output[2]));
}
但我不知道如何以通用方式指定 N
和 M
的所有组合。我假设可变参数模板会出现在某个地方,但我需要一些指导。
任何模板递归背后的基本思想都是一次处理一个参数,在函数上递归并删除一个输入类型参数,然后在模板参数列表为空时终止。
处理两个可变类型列表的一种常见方法是定义一个 "pack" 您可以专门研究的类型,其中包采用可变数量的模板参数。这使您能够轻松分离多组可变类型参数。
在这里,我通过声明 type_pack
没有任何实现来演示这个例子(这很好,我们只将它用作一个类型,我们从不实例化它)然后我声明 [=12= 的多个特化] 旨在:
- 从两个列表中剥离第一个类型并执行
dynamic_cast
。 - 通过删除相关包类型的第一个类型参数来确定下一步。
- 当第一个包变空时过渡到第二个包。
- 将处理(转换)的参数传递到下一步。
- 最后,当两个包都为空时,使用计算出的参数调用
bar()
。
template <typename...>
struct type_pack;
// Base declaration that we will specialize.
template <int, int, typename, typename>
struct foo_fn;
// Specialization handling when the first pack is not empty.
template <int OffsetA, int OffsetB, typename THead, typename... T1, typename... T2>
struct foo_fn<OffsetA, OffsetB, type_pack<THead, T1...>, type_pack<T2...>> {
template <typename... Args>
static void f(foovec_t const & input, foovec_t const & output, Args && ... args) {
return foo_fn<
OffsetA + 1,
OffsetB,
type_pack<T1...>,
type_pack<T2...>
>::f(input, output, std::forward<Args>(args)..., dynamic_cast<THead *>(input[OffsetA]));
}
};
// Specialization handling when the first pack is empty and the second
// pack is not empty.
template <int OffsetA, int OffsetB, typename THead, typename... T>
struct foo_fn<OffsetA, OffsetB, type_pack<>, type_pack<THead, T...>> {
template <typename... Args>
static void f(foovec_t const & input, foovec_t const & output, Args && ... args) {
return foo_fn<
OffsetA,
OffsetB + 1,
type_pack<>,
type_pack<T...>
>::f(input, output, std::forward<Args>(args)..., dynamic_cast<THead *>(output[OffsetB]));
}
};
// Specialization handling the terminating case (all packs empty).
template <int OffsetA, int OffsetB>
struct foo_fn<OffsetA, OffsetB, type_pack<>, type_pack<>> {
template <typename... Args>
static void f(foovec_t const &, foovec_t const &, Args && ... args) {
bar(std::forward<Args>(args)...);
}
};
// Helper type to provide the two initial integer values.
template <typename, typename>
struct foo;
template <typename... T1, typename... T2>
struct foo<type_pack<T1...>, type_pack<T2...>> {
static void f(foovec_t const & input, foovec_t const & output) {
foo_fn<0, 0, type_pack<T1...>, type_pack<T2...>>::f(input, output);
}
};
您可以在第二个示例中将其称为 foo<type_pack<DerivedFooData, Other>, type_pack<DerivedFooData, Other, Other>>::f(container, container2)
。请注意,您不必提供任何尺寸;这些是根据每包的大小推断的。
参见 this demo 并注意类型不匹配的指针参数作为空值出现。
我不会尝试定义 bar()
,因为我假设您已经这样做了,或者知道如何去做。我示例中的 bar()
只接受特定的指针类型(为了测试转换是否正确执行)。
此代码仅使用 C++11 功能。
请注意,std::forward
不是绝对必要的,因为转换值始终是指针。但是,在转发可变大小的参数列表时养成使用它的习惯是很好的。如果值很大 strings/vectors 那么每一步转发都会消除大量无用的复制。
一点也不优雅(并且不确定确切的索引)但是......下面的东西(假设你可以使用 C++14)应该可以工作(如果我理解正确的话你想要什么)
template <std::size_t Dim1, typename ... Ts, std::size_t ... Is>
void foo_helper (std::index_sequence<Is...>, std::vector<FooData*> inV,
std::vector<FooData*> outV)
{ bar( dynamic_cast<Ts*>(Is < Dim1 ? inV[Is] : outV[Is-Dim1])... ); }
template <std::size_t Dim1, std::size_t Dim2, typename ... Ts>
void foo (std::vector<FooData*> inV, std::vector<FooData*> outV)
{ foo_helper<Dim1, Ts...>
(std::make_index_sequence<Dim1+Dim2>{}, inV, outV); }
我知道 C++20 对您来说太新了,但是为了好玩,我将向您展示如何使用新的 C++20 lambda 模板功能来避免使用辅助函数
// from C++20: foo_helper() isn't needed anymore
template <std::size_t Dim1, std::size_t Dim2, typename ... Ts>
void foo (std::vector<FooData*> inV, std::vector<FooData*> outV)
{ [&]<std::size_t ... Is>(std::index_sequence<Is...>)
{ bar( dynamic_cast<Ts*>(Is < Dim1 ? inV[Is] : outV[Is-Dim1])... ); }
(std::make_index_sequence<Dim1+Dim2>{}); }
下面是一个完整的编译C++14的例子
#include <vector>
#include <type_traits>
struct FooData { virtual ~FooData() {}; };
class DerivedFooData: public FooData { };
class Other : public FooData { };
void bar (DerivedFooData*, DerivedFooData*) {}
void bar (DerivedFooData*, Other*, DerivedFooData*, Other*, Other*) {}
template <std::size_t Dim1, typename ... Ts, std::size_t ... Is>
void foo_helper (std::index_sequence<Is...>, std::vector<FooData*> inV,
std::vector<FooData*> outV)
{ bar( dynamic_cast<Ts*>(Is < Dim1 ? inV[Is] : outV[Is-Dim1])... ); }
template <std::size_t Dim1, std::size_t Dim2, typename ... Ts>
void foo (std::vector<FooData*> inV, std::vector<FooData*> outV)
{ foo_helper<Dim1, Ts...>
(std::make_index_sequence<Dim1+Dim2>{}, inV, outV); }
int main ()
{
DerivedFooData d1, d2, d3;
std::vector<FooData*> container1 {&d1, &d2};
std::vector<FooData*> container2 {&d1, &d2, &d3};
foo<1, 1, DerivedFooData, DerivedFooData>(container1, container2);
foo<2, 3, DerivedFooData, Other, DerivedFooData, Other, Other>
(container1, container2);
}
我更喜欢 "pack type" 语法而不是给出额外的数字,所以
foo<std::tuple<DerivedFooData, Other>, std::tuple<DerivedFooData, Other, Other>>
而不是你的:
foo<2, 3, DerivedFooData, Other, DerivedFooData, Other, Other>
您仍然可以:
template <typename Tuple, std::size_t ... Is1, std::size_t ... Is2, typename ... Ts>
void foo(std::index_sequence<Is1...>, std::index_sequence<Is2...>, Ts&&...args)
{
foo<std::tuple<std::tuple_element_t<Is1, Tuple>...>,
std::tuple<std::tuple_element_t<sizeof...(Is1) + Is2, Tuple>...>>(
std::forward<Ts>(args)...);
}
template <std::size_t N1, std::size_t N2, typename ... Ts>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
static_assert(N1 + N2 == sizeof...(Ts), "!");
foo<std::tuple<Ts...>>(std::make_index_sequence<N1>{},
std::make_index_sequence<N2>{},
input,
output);
}
使用您的语法。
现在使用主助手将每个矢量元素转换为 tuple
template <typename Pack> struct dynamic_cast_as_tuple;
template <typename ...Ts>
struct dynamic_cast_as_tuple<std::tuple<Ts...>>
{
template <typename T>
std::tuple<Ts*...> operator ()(const std::vector<T*>& v) const
{
return (*this)(v, std::index_sequence_for<Ts...>{});
}
private:
template <typename T, std::size_t ... Is>
std::tuple<Ts*...> operator ()(const std::vector<T*>& v, std::index_sequence<Is...>) const
{
return {dynamic_cast<Ts*>(v[Is])...};
}
};
然后,想要的功能是:
template <typename pack1, typename pack2>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
std::apply([](auto*... ps){ bar(ps...); },
std::tuple_cat(
dynamic_cast_as_tuple<pack1>{}(input),
dynamic_cast_as_tuple<pack2>{}(output))
);
}
std::index_sequence
是C++14和std::apply
c++17,但可以在C++11中实现。