使用模板避免类似的功能
Using templates to avoid similar functions
假设我有 2 个函数,它们对参数执行完全相同的操作,但使用不同的常量集来执行此操作。举一个过于简单的例子:
int foo1(int x){
return 3+4*x
}
int foo2(int x){
return 6-4*x
}
在实际应用中假设会有多个参数和constants/literals,当然计算会复杂得多。
为了简单和可维护性,我想将这两个函数重写为一个可以生成这两个函数的模板,这样我就可以调用 foo<1> 或 foo<2> 并生成正确的函数.我知道我可以做这样的事情:
int foo(int x, int funcType){
const int firstConst = (funcType==1) ? 3 : 6;
const int secondConst = (funcType==1) ? 4 : -4;
return firstConst+secondConst*x;
}
但是因为我总是在编译时知道我想使用哪个函数,所以我想使用模板来避免分支。有什么办法吗?
您可以使用特征 class 模板来单独管理 constants/literals,例如
template <int FuncType>
struct Constants;
template <>
struct Constants<1> {
static const int firstConst = 3;
static const int secondConst = 4;
};
template <>
struct Constants<2> {
static const int firstConst = 6;
static const int secondConst = -4;
};
template <int FuncType>
int foo(int x){
return Constants<FuncType>::firstConst + Constants<FuncType>::secondConst * x;
}
然后称它为
foo<1>(42);
foo<2>(42);
templatr<int funcType>
void foo(int x){
const int firstConst = (funcType==1) ? 3 : 6;
const int secondConst = (funcType==1) ? 4 : -4;
return firstConst+secondConst*x;
}
在任何非零优化设置下值得使用的编译器都不会有上述模板函数的运行时分支。
而且它通常比 traits classes.
更容易阅读
您通常可以从这种技术中获得乐趣,编写可编译为紧密操作的长分支代码。如果您的代码很好地分解成多个部分(例如 bool do_foo
作为模板参数),那么它的扩展性相当好。
超出此范围您可能希望避免维护数字 ID 的中央列表。通过标记分派到启用 constexpr ADL 的特征函数来查找特征,或者将模板非类型指针指向 constexpr 结构,两者都可以通过分布式函数子类型声明为您带来零开销效果。
最后,你可以直接传递一个特征class:
template<class Traits>
void foo(int x){
return x*Traits::z+Traits::y;
}
或
template<class Traits>
void foo(int x){
return x*Traits{}.z+Traits{}.y;
}
甚至
template<class Traits>
void foo(int x, Traits traits={}){
return x*traits.z+traits.y;
}
视具体需要而定。
与 traits 案例类似,你可以这样做:
template <int a, int b>
int foo(int x)
{
return a * x + b;
}
int foo1(int x){
return foo<4, 3>(x);
}
int foo2(int x){
return foo<-4, 6>(x);
}
假设我有 2 个函数,它们对参数执行完全相同的操作,但使用不同的常量集来执行此操作。举一个过于简单的例子:
int foo1(int x){
return 3+4*x
}
int foo2(int x){
return 6-4*x
}
在实际应用中假设会有多个参数和constants/literals,当然计算会复杂得多。 为了简单和可维护性,我想将这两个函数重写为一个可以生成这两个函数的模板,这样我就可以调用 foo<1> 或 foo<2> 并生成正确的函数.我知道我可以做这样的事情:
int foo(int x, int funcType){
const int firstConst = (funcType==1) ? 3 : 6;
const int secondConst = (funcType==1) ? 4 : -4;
return firstConst+secondConst*x;
}
但是因为我总是在编译时知道我想使用哪个函数,所以我想使用模板来避免分支。有什么办法吗?
您可以使用特征 class 模板来单独管理 constants/literals,例如
template <int FuncType>
struct Constants;
template <>
struct Constants<1> {
static const int firstConst = 3;
static const int secondConst = 4;
};
template <>
struct Constants<2> {
static const int firstConst = 6;
static const int secondConst = -4;
};
template <int FuncType>
int foo(int x){
return Constants<FuncType>::firstConst + Constants<FuncType>::secondConst * x;
}
然后称它为
foo<1>(42);
foo<2>(42);
templatr<int funcType>
void foo(int x){
const int firstConst = (funcType==1) ? 3 : 6;
const int secondConst = (funcType==1) ? 4 : -4;
return firstConst+secondConst*x;
}
在任何非零优化设置下值得使用的编译器都不会有上述模板函数的运行时分支。
而且它通常比 traits classes.
更容易阅读您通常可以从这种技术中获得乐趣,编写可编译为紧密操作的长分支代码。如果您的代码很好地分解成多个部分(例如 bool do_foo
作为模板参数),那么它的扩展性相当好。
超出此范围您可能希望避免维护数字 ID 的中央列表。通过标记分派到启用 constexpr ADL 的特征函数来查找特征,或者将模板非类型指针指向 constexpr 结构,两者都可以通过分布式函数子类型声明为您带来零开销效果。
最后,你可以直接传递一个特征class:
template<class Traits>
void foo(int x){
return x*Traits::z+Traits::y;
}
或
template<class Traits>
void foo(int x){
return x*Traits{}.z+Traits{}.y;
}
甚至
template<class Traits>
void foo(int x, Traits traits={}){
return x*traits.z+traits.y;
}
视具体需要而定。
与 traits 案例类似,你可以这样做:
template <int a, int b>
int foo(int x)
{
return a * x + b;
}
int foo1(int x){
return foo<4, 3>(x);
}
int foo2(int x){
return foo<-4, 6>(x);
}