避免为没有默认的参数创建临时变量?

时间:2017-03-22 16:18:40

标签: c++ c++14 c++17

这听起来可能是一个疯狂的问题。

在我的C ++代码中,我创建了一个像这样的方法

void Func(int & param_1, bool & param_2, float & param_3, double & param_4) {
   //some logic
}

所以我在main中调用方法如下:

int i_val;
bool b_val;
float f_val;
double d_val;

//invoke Func here
Func(i_val, b_val, f_val, d_val);

问题:有没有办法避免创建临时变量i_val, b_val, f_val等等?并在调用本身的行中创建它们?

我只对创建i_valb_val感兴趣并获取其值。 f_vald_val对我来说是不必要的,但在其他一些与我无关的电话中是必要的。有没有办法避免创建临时变量只是为了将每个参数传递给调用?

我知道我们可以将最后2个参数作为默认参数,但是使用默认参数会使函数参数对调用者无视。有没有办法没有使最后2个参数默认?在调用方法

时,动态创建float和double变量

我明白我会得到关于为什么你不想使用默认参数而只是检查是否有可能的交叉问题:)

5 个答案:

答案 0 :(得分:6)

当你有四个输出参数时,就像你在这里一样:

void Func(int & param_1, bool & param_2, float & param_3, double & param_4);

这表明你真的想要返回一个有4个成员的对象,比如:

std::tuple<int, bool, float, double> Func();

或:

struct X {
    int some;
    bool meaningful;
    float names;
    double here;
};

X Func();

这样,你可以写:

auto res = Func();

然后只使用您想要的字段。

在C ++ 17中,结构化绑定可以是:

auto [ival, bval, _1, _2] = Func();

没有一种明确的方式表达你不关心第三和第四位成员的观念,但这并不是坏事。

答案 1 :(得分:4)

C ++经验法则: 当你强制要求时引用 你做一些可选的东西时的指针。当然,该函数中的代码需要考虑可以传递的nullptr。

成功

void Func(int* pParam_1, bool* pParam_2, float* pParam_3, double* pParam_4) {
// some logic
}

答案 2 :(得分:2)

使用非const引用,例如你有,没有。您可以考虑函数重载,返回包含这些值的struct,甚至从C ++ 11开始返回std::tuple。 (请注意,std::tuple在C ++ 14中得到了显着的升级。)

如果您创建了引用const * 并依赖于返回的结果值,那么它会更好。然后,您可以将 anonymous temporaries 传递给您在呼叫站点的功能:

/*something*/ Func(const int& param_1, const bool& param_2, /*etc*/) {
}

并使用Func(1, true)等进行调用。使用const引用,您甚至可以提供默认值:

/*something*/ Func(const int& param_1 = 1, const bool& params_2 = true, /*etc*/){

* 有些编译器允许匿名临时的绑定作为扩展/无意的错误,非const引用。但如果我是你,我不会依赖它。

答案 3 :(得分:1)

你可以,但是你必须要知道虚拟临时工作会在完整表达结束时死掉:

template <typename T>
T& stay(T&& x = T()) {
    return x;
}

void Func(int& param_1, bool& param_2, float& param_3, double& param_4) {
   //some logic
}

int main() {
    int arg_1; float arg_3;
    Func(arg_1, stay<bool>(), arg_3, stay(42.0));
}

stay将rvalue转换为左值,因此与std::move相反。

答案 4 :(得分:1)

sort of create the float and double variables on the fly at the moment of invoking the method

To do this, create an anonymous temporary with the value you want. Then cast it so you can bind to an lvalue reference.

template<class T>
T& as_lvalue(T&& t={}){return t;}

void Func(int& param_1, bool& param_2, float& param_3, double& param_4) {
}

int main() {
  int a1;
  float a3;
  Func(a1, as_lvalue(false), a3, as_lvalue(3.14));
}

as_lvalue takes an rvalue (or lvalue!) and casts it to an lvalue of the same type. It can then bind to an lvalue reference, and be discarded at the end of the expression.

If you don't care what their value is you can even do:

int main() {
  int a1;
  float a3;
  Func(a1, as_lvalue<bool>(), a3, as_lvalue<double>());
}

Note that using reference parameters as pure-out parameters is a bit of code smell. When you use them, reference parameters should be in-out parameters; their value coming in and out should have use.

Parameters where you both carry data in and take it out are also dangerous, as it makes it harder to reason about the behavior of the function.

One approach that is extremely clean is to take and return a struct.

struct FuncArgs {
  int param_1;
  bool param_2;
  float param_3;
  double param_4;
};
FuncArgs Func(FuncArgs) {
  //some logic
}

people can call it like this:

Func({3, true, 42.f, 3.14})

and get the return data like this:

auto r = Func({3, true, 42.f, 3.14})
int param1 = r.param1;
bool param2 = r.param2;

ignoring the returned param3 and param4.

In C++17, they can even do:

auto[param1, param2, donotcare, alsodonotcare] = Func({3, true, 42.f, 3.14});

sadly there is no way to completely skip the donotcare and alsodonotcare fields.

If, however, your data is pure-out results, we just strip the FuncArgs arguments.

Now, modifying every location that calls Func may be annoying; but thanks to the wonder of overloading, we can just write:

inline FuncArgs Func() {
  FuncArgs args;
  Func(args.param1, args.param2, args.param3, args.param4);
  return args;
}

or

inline FuncArgs Func(FuncArgs args) {
  Func(args.param1, args.param2, args.param3, args.param4);
  return args;
}

and both happily live beside each other. Existing code need not be touched; new code is.