3

假设我有 2 个函数,它们对参数执行完全相同的操作,但使用不同的常量集来执行此操作。对于一个过于简单的例子:

int foo1(int x){
    return 3+4*x
}
int foo2(int x){
    return 6-4*x
}

在实际应用中假设会有多个参数和常量/文字,当然计算会复杂得多。为了简单起见,以及可维护性,我想将这两个函数重写为可以生成这两个函数的模板,以便我可以调用 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;
}

但由于我在编译时总是知道要使用哪个函数,所以我想使用模板来避免分支。有没有办法做到这一点?

4

3 回答 3

3

您可以使用特征类模板来分别管理常量/文字,例如

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);
于 2017-10-11T03:58:10.853 回答
3
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;
}

在任何非零优化设置下值得使用的编译器都不会有上述模板函数的运行时分支。

而且它通常比特征类更容易阅读。

一般来说,您可以享受这种技术带来的乐趣,编写冗长的分支代码,编译成紧凑的操作。如果您的代码很好地分解为多个部分(例如bool do_foo作为模板参数),这可以很好地扩展。

超出此范围,您可能希望避免维护数字 ID 的中央列表。通过标记分派到启用 constexpr ADL 的特征函数来查找特征,或 takijg 模板非类型指针指向 constexpr 结构,两者都可以通过分布式函数子类型声明为您提供零开销效果。

最后,您可以直接传递一个特征类:

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

视具体需要而定。

于 2017-10-11T04:04:40.667 回答
1

与特征案例类似,您可以执行以下操作:

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);
}
于 2017-10-11T08:04:04.410 回答