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

Avoid creating temporary vars for params which are not defaulted?

本文关键字:变量 创建 参数 默认      更新时间:2023-10-16

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

在我的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 个参数默认?在调用方法的那一刻动态创建浮点数和双精度变量

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

当你有四个out 参数时,就像你在这里所做的那样:

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();

没有一种明确的方式来表达你不关心第 3 和第 4 成员的概念,但这还不错。

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

来得及

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

对于像您这样的非const引用,没有。您可以考虑函数重载,返回包含这些值的struct,甚至是从 C++11 开始的std::tuple。(请注意,std::tuple在 C++14 中得到了重大升级。

如果您将引用设为*const并依赖返回的值来获取结果,那会让事情变得更好。然后,您可以在调用站点将匿名临时传递给您的函数:

/*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相反。

调用方法时动态创建浮点数和双精度变量

为此,请使用所需的值创建匿名临时。 然后强制转换它,以便可以绑定到左值引用。

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

请注意,使用引用参数作为纯参数有点代码气味。 使用它们时,引用参数应该是自出参数;它们的价值进出应该有用。

携带数据和取出数据的参数也很危险,因为它使得推理函数的行为变得更加困难。

一种非常干净的方法是获取并返回结构。

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

并获取如下返回数据:

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

忽略返回的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;
}

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