开云官网平台介绍 - 开云(中国)

2个月前 (04-21)

有时候出于种种目的,我们会用宏来写一些函数。 同题参见也就是说有人称之为宏函数。下面我们来模拟一场面试:

面试官

先来个简单的热热身,用宏实现求两个数最大值。

#define MAX(x, y) ((x) > (y) ? (x) : (y))

调用的时候:

int m = MAX(1, 10);

double m1 = MAX(1.0, 10.0)

借助三目运算符,这种一行的表达式很方便的可以做成宏函数。 对照阅读其实我觉得叫做函数宏可能更准确。因为从偏正短语的文法角度讲,『宏』才是中心词,应该放后面。好了。不上语文课了。

为开云官网平台介绍我想强调宏,而不是函数。 延伸阅读因为并不是所有带括号的宏,都能当成『函数』使用。比如:

#define FOO(name, score) score_map[name]=score; if (score > max_score) max_score = score;

宏就是机械地展开,带上参数,也就是增加了一点变量的味道,但并不是所有带参数的宏都能模拟『返回值』的效果,所以也就不是所有带参数的宏,都能当成宏函数……所谓的宏函数其实是一种巧合。

看到这里,你可能觉得我说了一堆废话,那我们继续:

面试官

在某项目中有这样一个老代码:

/* str: 待分割字符串
 * vec: 出参。存储分割后的字符串数组
 * sep: 分隔符 */
void str2vec(const string& str, vector& vec, char sep);

使用这个函数,实现一个宏:STR2VEC,使得它有这种效果:

auto vec = STR2VEC(str, sep);

题意解析一下,就是我们经常看到类似的split或者string转vector的函数。都需要这样使用:

string str = "abc:xxxx:123";

vector vec;
str2vec(str, vec, ':');

单就这个需求来说,不需要用宏之类的。定义一个重载函数,比较好转换。当然这是一个抽象后的问题,大概知道有时候有这样一类需求,不是像三目运算符那样可以一行求值,当成函数用。需要一些额外操作才能出值。这时候怎么变成宏函数呢?

既然2020年了,那么肯定有C++11,那便有lambda了。lambda很多人都有用。通常我们调用lambda需要先定义,再调用,比如:

 auto fun = [](const string& str, char sep) {
 vector vec;
 str2vec(str, vec, sep);
 return vec;
 };
 auto v = fun("abc:xxxx:123", ':');

可是这个怎么结合宏函数,变成一行调用的形式呢?

今天来介绍一种『原地调用』的lambda写法。

上述两个语句其实可以合并:

 [](const string& str, char sep) {
 vector vec;
 str2vec(str, vec, sep);
 return vec;
 }("abc:xxxx:123", ':');

那么实现这个宏函数就不难了。

#define STR2VEC(STR, SEP) [](const string& str, char sep) { \
 vector vec;\
 str2vec(str, vec, sep);\
 return vec;\
 }(STR, SEP);

就可以完成这种调用了:

 auto v = STR2VEC("abc:xxxx:123", ':');

面试官

不错,你提了lambda,也提到了lambda可以原地调用。其实当lambda无参数的时候。写起来会更简单。

嗯。另外没错,再写一个版本:

#define STR2VEC(STR, SEP) [&] { \
 vector vec;\
 str2vec(STR, vec, SEP);\
 return vec;\
 }();

当无参的时候, [] 后面的参数声明可以直接省略了。与此同时是不是更简洁了。

lamba形式繁多,在无参的时候,可以省略参数列表。一般来说另外其实完整版的lamba是连返回值类型也要声明的。但在编译器能正确推导返回值类型的时候,通常我们省略了。看一个完全体的lambda表达式:

[] (int x, int y) -> int { int z = x + y; return z; }

面试官

我们继续,有一个策略类型Strategy,封装了很多计算操作。

有一个全局的策略map。可以通过策略名,找到对应的策略指针来指针。

需要写一个lambda内部有一系列拼装策略名称的逻辑(可以...)。当能走strategy_map中找到的时候,返回对应的策略指针,可能存在找不到的场景。

看下我这个代码有开云官网平台介绍问题:

// 声明
unordered_map strategy_map;
...
// 某文件中的lambda

auto fun = [&] {

 //引用捕获了一些外部数据,用以拼装得到策略名称name
 // ... 获取name的逻辑
 auto it = strategy_map.find(name);
 if (it != strategy_map.end();) {
 return it->second;

 } else {
 return nullptr;
 }

};
...
auto stg = fun();
if (stg != nullptr) {
 stg->run();
}

问题就是简化版的lambda,无返回值声明。然而这个lambda内部可能返回两种类型。一种是Strategy*,另外一种是nullptr_t,编译会失败。

面试官

那可以怎么修改呢?

我想到两种办法。因此第一种是加上返回值声明,但是要加上返回值声明。参数列表也要一起补全。

auto fun = [&]() -> Strategy*{
 //引用捕获了一些外部数据,用以拼装得到策略名称name
 // ... 获取name的逻辑
 auto it = strategy_map.find(name);
 if (it != strategy_map.end();) {
 return it->second;
 } else {
 return nullptr;
 }
};

还有一种可以不加上参数声明和返回值声明。但是要修改一下策略map的设计,使之能够让编译器推导出类型。我觉得此处用智能指针可能更好。

// 声明
unordered_map> strategy_map;
...

auto fun = [&] {
 //引用捕获了一些外部数据,用以拼装得到策略名称name
 // ... 获取name的逻辑
 auto it = strategy_map.find(name);
 if (it != strategy_map.end();) {
 return it->second;
 } else {
 return shared_ptr(); 
 }
};

shared_ptr<>()直接构造出来的对象和nullptr做比较返回是相等的!

这点和make_shared<>()不同哦!


原文地址:https://cloud.tencent.com/developer/article/1915062

你可能想看:

分享给朋友:

继续阅读

若需了解「开云官网平台介绍」的上下文,可结合站内栏目与相关篇目交叉阅读。

栏目用于追新,文内链接用于对照细节。两者配合可减少漏读与重复检索。

继续浏览时优先选择同栏目或相近主题,避免被站外镜像带偏。

频道列表

string · wiki · 科技 · 文化

页面按栏目组织。可先看导读,再进入相关篇目或返回列表。