C++ 用于强制转换多个参数的模板函数

C++ 用于强制转换多个参数的模板函数,c++,c++11,templates,variadic-templates,template-meta-programming,C++,C++11,Templates,Variadic Templates,Template Meta Programming,我正在尝试编写一个模板函数,该函数将根据其模板参数执行一组动态_强制转换。我有以下几点要说明: class FooData { public: virtual ~FooData() {}; }; class DerivedFooData: public FooData{}; class Other: public FooData{}; void bar(DerivedFooData* d1, DerivedFooData* d2) {} void bar(DerivedFooData*

我正在尝试编写一个模板函数,该函数将根据其模板参数执行一组动态_强制转换。我有以下几点要说明:

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的所有组合。我假设可变模板会出现在某个地方,但我需要一些指导。

任何模板递归背后的基本思想是一次处理一个参数,在移除一个输入类型参数的情况下递归函数,然后在模板参数列表为空时终止

处理两个可变类型列表的一种常见方法是定义一个可以专门化的包类型,其中包采用可变数量的模板参数。这使您能够轻松地分离多组可变类型参数

在这里,我通过声明type_pack来演示这个示例,没有任何实现,这很好,我们只将它用作一个类型,从不实例化它,然后我声明了foo_fn的多个专门化,这些专门化旨在:

从两个列表中的一个列表中剥下第一个类型并执行动态_转换。 通过删除相关包类型的第一个类型参数来确定下一步。 当第一个包变空时,转换到第二个包。 将已处理的强制转换参数传递到下一步。 最后,当两个包都为空时,使用计算参数调用bar。 在第二个示例中,您可以将其称为foo::fcontainer,container2。请注意,您不必提供任何尺寸;这些是根据每个包装的尺寸推断出来的

请参阅并注意,类型不匹配的指针参数为null

我不会试图定义bar,因为我假设您已经这样做了,或者知道如何做。为了测试强制转换是否正确执行,我的示例中的条只接受特定的指针类型

此代码仅使用C++11功能


请注意,std::forward并不是严格必需的,因为强制转换值始终是指针。但是,在转发可变大小的参数列表时,养成使用它的习惯是很好的。如果值是巨大的字符串/向量,那么在每一步进行转发将消除大量无用的复制。

任何模板递归背后的基本思想是一次处理一个参数,在删除一个输入类型参数的情况下递归函数,然后在模板参数列表为空时终止

处理两个可变类型列表的一种常见方法是定义一个可以专门化的包类型,其中包采用可变数量的模板参数。这使您能够轻松地分离多组可变类型参数

在这里,我通过声明type_pack来演示这个示例,没有任何实现,这很好,我们只将它用作一个类型,从不实例化它,然后我声明了foo_fn的多个专门化,这些专门化旨在:

从两个列表中的一个列表中剥下第一个类型并执行动态_转换。 通过删除相关包类型的第一个类型参数来确定下一步。 当第一个包变空时,转换到第二个包。 将已处理的强制转换参数传递到下一步。 最后,当两个包都为空时,使用计算参数调用bar。 在第二个示例中,您可以将其称为foo::fcontainer,container2。请注意,您不必提供任何尺寸;这些是根据每个包装的尺寸推断出来的

请参阅并注意,类型不匹配的指针参数为null

我不会试图定义bar,因为我假设您已经这样做了,或者知道如何做。为了测试强制转换是否正确执行,我的示例中的条只接受特定的指针类型

此代码仅使用C++11功能


请注意,std::forward并不是严格必需的,因为强制转换值始终是指针。但是,在转发可变大小的参数列表时,养成使用它的习惯是很好的。如果值是巨大的字符串/向量,那么每一步的转发都将消除大量无用的复制。

一点也不优雅,也不确定确切的索引,但是。。。如果我正确理解了您想要什么,那么下面给出的可以使用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模板功能来避免使用helper函数

// 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);
 }

一点也不优雅,也不确定确切的索引,但是。。。如果我正确理解了您想要什么,那么下面给出的可以使用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模板功能来避免使用 f辅助函数

// 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);
 }

我更喜欢包类型语法,而不是给出额外的数字,所以

foo<std::tuple<DerivedFooData, Other>, std::tuple<DerivedFooData, Other, Other>>
然后,想要的功能是:

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_序列是C++14和std::apply C++17,但可以在C++11中实现。

我更喜欢包类型语法,而不是给出额外的数字,所以

foo<std::tuple<DerivedFooData, Other>, std::tuple<DerivedFooData, Other, Other>>
然后,想要的功能是:

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_序列是C++14和std::apply C++17,但可以在C++11中实现。

你能解释一下N和M参数应该控制什么吗?N和M将是foosFWIW中输入和输出的第一个和第二个参数向量的大小,如果你使用动态_cast,你通常会有设计缺陷。如果你要使用它,你真的应该检查返回值以确保它不是null ptr,否则你将面临未定义的行为。如果N和M是向量的大小,并且必须在编译时知道它们,为什么你要使用std::vector而不是std::array?@NathanOliver同意,在cast上,请尽量少举一个例子:-你能解释一下N和M参数应该控制什么吗?N和M是foosFWIW中输入和输出的第一个和第二个参数向量的大小,如果你使用dynamic_cast,你通常会有设计缺陷。如果你要使用它,你真的应该检查返回值以确保它不是null ptr,否则你将面临未定义的行为。如果N和M是向量的大小,并且必须在编译时知道它们,为什么你要使用std::vector而不是std::array?@NathanOliver同意,在cast上,我只是想尽量少举一个例子:-巧妙地使用std::index_序列,我喜欢。巧妙地使用std::index_序列,我喜欢。