Avoid creating temporary variables for parameters that are not defaults?

This might sound crazy.

in my c ++ code, i created a method like this

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

      

So, I am calling the method in main

as shown below:

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

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

      

Question: Is there a way to avoid creating temporary variables i_val, b_val, f_val

, etc.? And create them in the call line itself?

I'm only interested in creating i_val

and b_val

and getting their values. f_val

and are d_val

not needed for me, but are needed in another call that does not concern me here. Is there a way to avoid creating temporary vars just to pass each parameter to the call?

I know we can make the last 2 params as default parameters , but with default parameters, the function parameters are ignored by the caller. Is there a way that doesn't set the last 2 parameters by default? type of creation of float and double variables "on the fly" at the time of method call

I understand that I will have cross questions about why you don't want to use the default parameters, but just check if there is a possibility :)

+3


source to share


5 answers


When you have four parameters like you do here:

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

      

This suggests that you actually want to return a single object with 4 members, for example:

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

      

or

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

X Func();

      

Thus, you can simply write:



auto res = Func();

      

and then just use the fields you want.


In C ++ 17 with structured bindings that can be:

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

      

There isn't really a clear way to express the opinion that you don't care about 3rd and 4th terms, but that's not bad.

+6


source


C ++ Symbols: References when you do something mandatory when you do something optional. Of course, the code in this function must take into account the nullptr that can be passed.

Do it



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

      

+4


source


With a link const

like yours, no. You might consider an overload function returning struct

containing these values, or even std::tuple

from C ++ 11 onwards. (Note that it std::tuple

gets a significant update in C ++ 14.)

It would be better if you make your references const

* and rely on return values ​​for the results. Then you can pass anonymous temporary data to your function in the calling site:

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

      

and calling with help Func(1, true)

etc. const

You can even provide default values ​​with links :

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

      


* Some compilers allow anonymous binding to the temporary link is not const

as an extension of the errors / unintentional. But I wouldn't rely on it if I were you.

+2


source


You can, but you should be aware that dummy temp files will die at the end of the full expression:

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

converts rvalues ​​to lvalues ​​and is therefore the opposite std::move

.

+1


source


type of creation of float and double variables "on the fly" at the time of method call

To do this, create an anonymous temporary value with the value you want. Then drop it so you can bind to the 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 converts it to an lvalue of the same type. It can then bind to the lvalue reference and discard 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 parameters is a bit of a code smell. When you use them, the reference parameters must be input parameters; their value, in and out, must have application.

Parameters where you both move data in and out are also dangerous as it makes it difficult to reason about the behavior of a function.

One very clean approach is to take and return a structure.

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 returned data like this:

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

      

ignoring returned param3

and param4

.

In C ++ 17, they can even do:

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

      

Unfortunately, there is no way to completely skip the donotcare

and fields alsodonotcare

.

If, however, your data is pure results, we'll just strip the arguments FuncArgs

.

Now changing any place calling Func

can be annoying; but thanks to the surprise of the overload, 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 live happily next to each other. The existing code does not need to be touched; new code.

+1


source







All Articles