我正在使用由其他人编写的C ++库,该库(不幸的是imo)实现了封装在类中的几种算法,这些类的行为可以通过作为模板参数传递的参数进行微调(这些参数通常也是类)。因此,例如,可能会有一个这样的类模板:
template<typename Param1, typename Param2, typename Param3>
class Foo {
// ...
};
我想使用该库编写一个程序,该程序需要根据运行时信息创建Foo
的实例。我的问题是,假设可以为N1
,N2
和N3
传递Param1
,Param2
和Param3
有效类型,当我要实例化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_1
是Param1
的有效类型,依此类推。如何才能更优雅地实现这一点? (最好仅使用C ++ 11或最多使用C ++ 17功能)。
答案 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
}