避免为非默认参数创建临时变量?

Avoid creating temporary vars for params which are not defaulted?

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

在我的 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 变量

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

C++ 经验法则: 强制执行时的参考 当您将某些内容设为可选时的指针。当然,该函数中的代码需要考虑可以传递的 nullptr。

成功

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

当您有 四个 输出参数时,就像您在此处所做的那样:

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

这表明您真的想要 return 一个具有 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();

虽然没有明确的方式来表达您不关心第 3 名和第 4 名成员的想法,但这还不错。

对于像您这样的非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 引用作为扩展/无意的错误.但如果我是你,我不会依赖它。

你可以,但你必须知道虚拟临时对象会在完整表达式结束时消失:

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 将右值转换为左值,因此与 std::move 相反。

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

为此,请使用您想要的值创建一个匿名临时文件。然后对其进行转换,以便您可以绑定到左值引用。

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 获取一个右值(或左值!)并将其转换为相同类型的左值。然后它可以绑定到左值引用,并在表达式末尾被丢弃。

如果您不关心它们的价值,您甚至可以这样做:

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

请注意,使用参考参数作为纯输出参数有点代码味道。使用时,引用参数应该是in-out参数;他们进出的价值应该有用。

同时传入和传出数据的参数也很危险,因为它更难推断函数的行为。

一种非常干净的方法是采用 return 结构。

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

人们可以这样称呼它:

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

并像这样获取 return 数据:

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

忽略 returned param3param4

在 C++17 中,他们甚至可以做到:

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

遗憾的是,无法完全跳过 donotcarealsodonotcare 字段。

但是,如果您的数据是纯粹的结果,我们将删除 FuncArgs 个参数。

现在,修改每个调用 Func 的位置可能很烦人;但是感谢重载的奇迹,我们可以写:

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

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

两人幸福地生活在一起。无需修改现有代码;新代码是。