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

时间:2014-04-23 12:50:57

标签: c++ types arguments named-parameters

假设我有一个看起来像这样的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个参数会出现错误。

理想情况下,我希望检查在编译时完成,并且没有性能损失(即在一天结束时它们被视为普通的旧浮动或其他)。

4 个答案:

答案 0 :(得分:3)

这个怎么样:

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

当然,您在分配参数时仍然会陷入困境,但这种风险很难避免:)

答案 1 :(得分:2)

一个变体是每个" new"键入,然后使用宏使它们在优化的构建中消失。

沿着这些方向的东西(仅经过轻微测试,因此可能会有所不同):

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

答案 2 :(得分:1)

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

一种替代方法可能是使用构建器类和方法链接。 像:

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

您可以这样使用:

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

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

答案 3 :(得分:0)

实际上“错误的订单”是什么?在你的这个例子中

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”和“moo”而不是“a”和“b”怎么办?那么仅仅通过查看它们就不可能猜测订单是对还是错。

考虑到这一点,你至少可以做两件事。首先,是为参数提供有意义的名称,例如

float getTax( float price, float taxPercentage )

而不是

float getTax( float a, float b )

其次,在里面进行必要的检查:

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

可以进行更复杂的检查,例如制作仿函数,并明确设置它的参数,但在大多数情况下,只会使事情变得复杂而没有太多好处。