根据运行时参数,避免模板实例化的代码重复

时间:2019-07-14 17:45:55

标签: c++ templates

我正在使用由其他人编写的C ++库,该库(不幸的是imo)实现了封装在类中的几种算法,这些类的行为可以通过作为模板参数传递的参数进行微调(这些参数通常也是类)。因此,例如,可能会有一个这样的类模板:

template<typename Param1, typename Param2, typename Param3>
class Foo {
  // ...
};

我想使用该库编写一个程序,该程序需要根据运行时信息创建Foo的实例。我的问题是,假设可以为N1N2N3传递Param1Param2Param3有效类型,当我要实例化N1 x N2 x N3的不同的特殊化时,可能需要在代码中的某个位置创建最多Foo个分支。例如,假设我在运行时通过用户输入给了三个字符串,并且每个字符串都决定Foo的模板参数之一应为哪种类型,那么我将需要执行以下操作:

std::string s1, s2, s3;

// instantiate s1, s2, s3 from user input

if (s1 == "Param1_1" && s2 == "Param2_1" && s3 == "Param3_1") {
  Foo<Param1_1, Param2_1, Param3_1> foo;
} else if (s1 == "Param1_1" && s2 == "Param2_1" && s3 == "Param3_2") {
  Foo<Param1_1, Param2_1, Param3_2> foo;
}

// ...

Param1_1Param1的有效类型,依此类推。如何才能更优雅地实现这一点? (最好仅使用C ++ 11或最多使用C ++ 17功能)。

2 个答案:

答案 0 :(得分:3)

您可以为每种类型使用std::variant(c ++ 17),并为分发使用std::visit

template <typename T> struct Tag{ using type = T;};

std::variant<Tag<T1>, Tag<T2>, Tag<T3>/*..*/> getType(const std::string& s)
{
    if (s == "Param1") { return Tag<T1>{}; }
    else if (s == "Param2") { return Tag<T2>{}; }
    // ...
}

void bar(const std::string& s1, const std::string& s2, const std::string& s3)
{
    auto v1 = getType(s1);
    auto v2 = getType(s2);
    auto v3 = getType(s3);

    std::visit([](auto t1, auto t2, auto t3)
              {
                  Foo<typename decltype(t1)::type,
                      typename decltype(t2)::type,
                      typename decltype(t3)::type> foo;
                  /*..*/
               }, v1, v2, v3);
}

答案 1 :(得分:0)

从C ++ 11开始,您可以创建一系列函数(每种类型一个),以选择并传输正确的模板参数。最后一个调用的函数(在下面的示例中,第一个函数)可以创建Foo对象。

不太优雅,但是可以避免N1 x N2 x N3的乘法效果。

template <typename ... PrevParams>
void for_s3 (std::string const & s3)
 {
   if ( "Param3_1" == s3 )
      Foo<PrevParams..., Param3_1>{}
   else if ( "Param3_2" == s3 )
      Foo<PrevParams..., Param3_2>{};
   // other cases 
 }    

template <typename ... PrevParams>
void for_s2 (std::string const & s2, std::string const & s3)
 {
   if ( "Param2_1" == s2 )
      for_s3<PrevParams..., Param2_1>(s3);
   else if ( "Param2_2" == s2 )
      for_s3<PrevParams..., Param2_2>(s3);
   // other cases 
 }    

void for_s1 (std::string const & s1, std::string const & s2, std::string const & s3)
 {
   if ( "Param1_1" == s1 )
      for_s2<Param1_1>(s2, s3);
   else if ( "Param1_2" == s1 )
      for_s2<Param1_2>(s2, s3);
   // other cases 
 }

也许更优雅,更灵活(您可以轻松添加其他字符串和参数),但运行时成本更高,是一种双功能解决方案

template <typename ... Params>
void select_foo ()
 {
   Foo<Params...>  f;

   // do something with f
 }

template <typename ... PrevParams, typename ... Ts>
void select_foo (std::string const & s, Ts const & ... ts)
 {
   if ( "Param1_1" == s )
       select_foo<PrevParams..., Param1_1>(ts...);
   else ( "Param1_2" == s )
       select_foo<PrevParams..., Param1_2>(ts...);
   // other Param1_x cases
   else if ( "Param2_1" == s )
       select_foo<PrevParams..., Param2_1>(ts...);
   else ( "Param2_2" == s )
       select_foo<PrevParams..., Param2_2>(ts...);
   // other Param2_x cases
   else if ( "Param3_1" == s )
       select_foo<PrevParams..., Param3_1>(ts...);
   else ( "Param3_1" == s )
       select_foo<PrevParams..., Param3_2>(ts...);
   // other Param3_x cases
 }