如何防止函数参数以错误的顺序传递

How to guard against function arguments being passed in the wrong order?

本文关键字:顺序 错误 何防止 函数 参数      更新时间:2023-10-16

假设我有一个C++函数,如下所示:

double myfunction(double a, double b) {
    // do something
}

然后我这样称呼它:

double a = 1.0;
double b = 2.0;
double good_r = myfunction(a, b);
double bad_r = myfunction(b, a); // compiles fine

我想确保ab永远不会以错误的顺序提供。在C++中,确保这一点的最佳方法是什么?

其他语言允许命名参数,例如:

double good_r = myfunction(a=a, b=b);
double bad_r = myfunction(a=b, b=a); // mistake immediately obvious
double bad_r = myfunction(b=b, a=a); // compiles fine

或者,也许可以使用类型来部分解决问题,即

double my_type_safe_function(a_type a, b_type b) {
    // do something
}
a_type a = 1.0;
b_type b = 2.0;
double good_r = myfunction(a, b);
double bad_r = myfunction(b, a); // compilation error

编辑:有几个人问我说的"错误的顺序"是什么意思。我的意思是,在实际代码中,ab有一些意义。例如,自变量可能是heightwidth。它们之间的差异对于函数返回正确的结果非常重要。然而,它们都是浮动的,并且都具有相同的尺寸(即长度)。此外,他们没有"明显"的顺序。编写功能声明的人可以假定(width, height),并且使用该功能的人可以假设(height, width)。我想要一种方法来确保这不会错误地发生。有了两个参数,很容易小心顺序,但在一个大项目中,最多有6个参数,错误会悄悄出现

理想情况下,我希望检查在编译时进行,并且不会对性能造成影响(即,在一天结束时,它们被视为普通的旧浮动或其他什么)。

这个怎么样:

struct typeAB {float a; float b; };
double myfunction(typeAB p) {
// do something
  return p.a - p.b;
}
int main()
{
  typeAB param;
  param.a = 1.0;
  param.b = 2.0;
  float result = myfunction(param);
  return 0;
}

当然,当您分配参数时,您仍然可能会出错,但这种风险很难避免:)

变体是每个"新"类型有一个结构,然后使用宏使它们在优化的构建中消失。

沿着这些路线的东西(只是稍微测试了一下,所以可能会有点偏离):

#define SAFE 0
#if SAFE
#define NEWTYPE(name, type) 
    struct name { 
       type x; 
       explicit name(type x_) : x(x_) {}
       operator type() const { return x; }
    }
#else
#define NEWTYPE(name, type) typedef type name
#endif
NEWTYPE(Width, double);
NEWTYPE(Height, double);
double area(Width w, Height h)
{
    return w * h;
}
int main()
{
    cout << area(Width(10), Height(20)) << endl;
    // This line says 'Could not convert from Height to Width' in g++ if SAFE is on.
    cout << area(Height(10), Width(20)) << endl; 
}

我认为您已经提供了使用类型的最简单的解决方案。

一种替代方案可以是使用构建器类和方法链接。类似:

class MyfunctionBuilder {
  MyFunctionBuilder & paramA(double value);
  MyFunctionBuilder & paramB(double value);
  double execute();
  (...)
}

你会这样使用:

double good_r = MyFunctionBuilder().paramA(a).paramB(b).execute();

但这是很多额外的代码要写!

"错误订单"究竟是什么?在你的的这个例子中

double myfunction(double a, double b) {
    // do something
}
double a = 1.0;
double b = 2.0;
double good_r = myfunction(a, b);
double bad_r = myfunction(b, a);

你真的想知道这个是否是正确的顺序吗?如果变量被命名为"quapr"answers"moo"而不是"a"answers"b"呢?那么,仅仅通过观察它们就无法猜测顺序是对是错。

考虑到这一点,你至少可以做两件事。首先,是给自变量起一个有意义的全名,例如

float getTax( float price, float taxPercentage )

而不是

float getTax( float a, float b )

其次,在内部进行必要的检查:

float divide( float dividend, float divisor )
{
    if( divisor == 0 )
    {
         throw "omg!";
    }
}

可以进行更复杂的检查,例如生成一个函子,并显式设置它的参数,但在大多数情况下,这只是使事情复杂化,没有多大好处。