前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++17常用新特性(六)---lambda表达式的扩展

C++17常用新特性(六)---lambda表达式的扩展

作者头像
CPP开发前沿
发布2022-04-13 15:30:06
9330
发布2022-04-13 15:30:06
举报
文章被收录于专栏:CPP开发前沿

从C++11起就引入了lambda表达式,C++14又对其进行了丰富,开始支持使用泛型lambda。到现在的C++17 lambda的功能又进行了扩展。在C++17新特性中,主要支持了以下两种场景:

  • 在常量表达式中使用
  • 需要对当前对象的拷贝时使用,如不同的线程需要创建不同的对象。

1 constexpr lambda表达式

从C++17开始,lambda表达式会尽可能的隐式声明constexpr,在任何只使用有效的编译期上下文的lambda都有可能被用于编译期。这些上下文环境主要包含的场景有:只使用字面变量、没有静态类型、没有虚函数、没有异常捕获及new/delete的上下文环境。如在下面的代码中,因为在lambda中使用了static,表达式将会失去constexpr的能力。

代码语言:javascript
复制
int main(){
    auto squared2 = [](auto val) {
        static int calls = 0;
        return val*val;
    };
    std::array<int, squared2(5)> a; 
    std::cout << squared2(5) << '\n'; 
    return 0;
}

如上代码片段中,第6行代码将lambda中计算的值当做array数组的大小在编译时将会报错。因为在lambda中声明了一个static类型的变量,那么表达式也将失去constepr的能力,既不能在编译器使用。但是它依然可以在运行期使用,试着将第6行代码段进行注释,那么代码可以继续编译且打印出计算结果。

同样的,如果在上面代码中显示定义成constexpr。那么编译时程序将会报错如下:

如此,按照上面上面编译场景,在确认一个lambda表达式是否可用于编译期时就可以在表达式中使用constepr进行判断。如从C++17起,就可以按照以下方式使用:

代码语言:javascript
复制
int main(){
    auto squar = [](auto val) constexpr {
        return val*val;
    };
    return 0;
}

如果要在表达式中指明返回值类型,可以按照如下方式编写代码:

代码语言:javascript
复制
int main(){
    auto squar = [](auto val) constexpr -> int {
        return val*val;
    };
    return 0;
}

在看下面两种lambda表达式中使用constepr的代码段:

代码语言:javascript
复制
int main()
{
    auto squar1 = [](auto val) constexpr {
        return val*val;
    };
    constexpr auto squar2 = [](auto val) {
        return val*val;
    };
    return 0;
}

如上squqr1是在表达式中使用constexpr,squar2则是在变量前使用constexpr,他们表达的含义是不同的,第一种写法是在编译器执行,第二种表示的是在编译器就会对变量进行赋值。如果想同时使用可以按照下面的方式编写代码:

代码语言:javascript
复制
constexpr auto squar2 = [](auto val) constexpr{
        return val*val;
    };

2 constexpr lambda的使用

下面的例子主要演示了在lambda中使用constexpr,代码分别在编译期和运行期调用lambda的场景,代码如下:

代码语言:javascript
复制
auto hash = [](const char* str) {
    std::size_t hash = 5381;
    while (*str != '\0') {
        hash = hash * 33 ^ *str++; 
    }
    return hash;
};

emum hashV{
    hash(cat),
    hash(dog),
    hash(mow),
    hash(sheep)
};

int main(int argv,char * argc[])
{
    switch(argc[2]){
        case hash(cat):
            break;
        case hash(dog):
            break;
        case hash(mow):
            break;
        case hash(sheep):
            break;
    }
    return 0;
}

在上面的代码中,switch的条件是在运行时执行,case是在编译时执行。实际上在上面程序里面还可以使用函数,如下面的代码:

代码语言:javascript
复制
auto hashed = [](const char* str, auto combine) {
  std::size_t hash = 1437;
  while (*str != '\0') {
  hash = combine(hash, *str++); 
  }
  return hash;
};

在使用时按照如下方式只用即可:

代码语言:javascript
复制
constexpr std::size_t hv1{hashed("wine"), [](auto h, char c) {return h*33 + c;})};

3 向 lambda 传递 this 的拷贝

在C++11或者C++14中,如果要捕获this,可以通过值或者引用的方式进行。编写代码时可以按照下面的方式进行:

代码语言:javascript
复制
class CType {
private:
std::string name;
public:
CType(const std::string &_name):name(_name){
    std::cout<<"CType->"<<name<<endl;
}
void foo() const
{
    auto Type1 = [this] {std::cout << this->name << endl;};
    auto Type2 = [=] {std::cout << name << endl;}; 
    auto Type3 = [&] {std::cout << name << endl;};
    Type1();
    Type2();
    Type3();
    
}
};
int main()
{
    CType stType{"qwqw"};
    stType.foo();
    return 0;
}

如上代码所示程序将会输出正确的结果,但是也存在着问题,既当lambda的生命周期更长时,可能不会得到正确的结果。但是在C++14中,提出了一个解决方案,代码可以修改成如下所示:

代码语言:javascript
复制
void foo() const
{
    auto Type1 = [thisCopy=*this] {std::cout << thisCopy.name << endl;};
    Type1();
}

在C++17中,就可以显示的使用*this进行捕获,代码如下修改:

代码语言:javascript
复制
void foo() const
{
    auto Type1 = [*this] { std::cout << name << endl; };
    Type1();
}

当然,也可以在捕获this的时候捕获其它对象,如:

代码语言:javascript
复制
auto Type1 = [&,*this] { std::cout << name << endl; };

4 相关背景

constexpr lambda 由 Faisal Vali、 Ville Voutilainen和 Gabriel Dos Reis 在https://wg21.link/n4487中首次提出的。但是最终被接受的正式谈是由Faisal Vali、Jens Maurer、 Richard Smith 发表于https://wg21.link/p0170r1。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CPP开发前沿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档