lambda技巧之—如何在有多个判断分支的情况下,还能优雅的使用auto ?
假设有下面这样的代码:
在一个班级里面只有一个学生 a,每天他都来上课学习。
class Student
{
public:
void study() {};
};
class _Class
{
public:
Student a;
};
int main(int argc, char* argv[])
{
_Class clazz;
auto& student = clazz.a;
student.study();
return 0;
}
我们会在代码中使用 auto& 来让编译器自己推导返回值类型,并通过 & 符号避免产生拷贝。
有一天,班级里来了另一个学生b,但是因为某些原因a只能周一、周三、周五来上课,而b只能周二、周四来上课,所以我们的学生类变成了这样:
class Student
{
public:
void study() {};
};
class _Class
{
public:
Student a;
Student b;
};
而main函数因为增加了条件判断,导致我们不能再使用auto&,必须将其变为指针,因此我们的代码变成了这样:
int main(int argc, char* argv[])
{
_Class clazz;
int mondy = 1;
Student* student; // auto& student = clazz.a;
if (mondy % 2 == 1) {
student = &clazz.a;
} else {
student = &clazz.b;
}
student->study();
return 0;
}
很无奈的情况下我们必须将 auto& 退化成指针,本来简单的 clazz.a 的引用也要变成 &clazz.a ,且操作符 “.” 也要变成"->“,此时不知道屏幕前的你是否也感受到了一点代码的"臭味道”。
那如何保留我们之前 auto& 的写法呢?
方法1:
使用三目运算符,如下:
auto& student = mondy % 2 == 1 ? clazz.a : clazz.b;
在简单的情况下,使用三目运算符就解决了我们的烦恼。但是稍微增加一点复杂度呢。比如又来了个学生c呢?或者我想在条件判断里面增加其他代码呢? 三目运算符就不能满足要求了。这时候就要来到方法2.
方法2:
首先我们要知道一个关于lambda的知识点,那就是可以支持立即执行。
[]() {
//....
} ();
像上面这样,在普通lambda后面加上(),不仅定义了一个匿名lambda,还立即执行了这个lambda。这被称为:IIFE全称为Immediately Invoked Function Express-立即执行函数(表达式)。来源于 javascript,详见:
An IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined. The name IIFE is promoted by Ben Alman in his blog.
https://developer.mozilla.org/en-US/docs/Glossary/IIFE
然后我们就可以将代码改成这样:
int main(int argc, char* argv[])
{
_Class clazz;
int mondy = 1;
auto& student = [&]() -> decltype(clazz.a)& { //C++ 11
if (mondy % 2 == 1) {
return clazz.a;
} else {
return clazz.b;
}
}();
// auto& student = [&]() -> auto& { //C++ 14
// if (mondy % 2 == 1) {
// return clazz.a;
// } else {
// return clazz.b;
// }
// }();
student.study();
return 0;
}
C11的写法中,decltype 可以根据捕获参数 clazz 推导出此lambda的返回值类型,而如果使用C14即以上,lambda返回值直接使用auto&就行了。
Perfect,得益于 IIFE 的好处,我们幸运的保留的 auto& student的写法,而不用再改成丑陋的指针了。
关注公众号 QTShared,带你探索更多 C++/QT 相关知识。