分类目录归档:C++17

C++17中的string_view

基本用法

C++17中的string_view是一个char数据的视图或者说引用,它并不拥有该数据,是为了避免拷贝,因此使用string_view可以用来做性能优化。你应该用string_view来代替const char和const string了。string_view的方法和string类似,用法很简单:

构造string_view的时候用char*和长度来构造,这个长度可以自由确定,它表示string_view希望引用的字符串的长度。因为它只是引用其他字符串,所以它不会分配内存,不会像string那样容易产生临时变量。我们通过一个测试程序来看看string_view如何来帮我们优化性能的。

我们可以通过字面量””sv来初始化string_view.
string_view的substr和string的substr相比,快了50多倍,根本原因是它不会分配内存。

string_view的生命周期

由于string_vew并不拥有锁引用的字符串,所以它也不会去关注被引用字符串的生命周期,用户在使用的时候需要注意,不要将一个临时变量给一个string_view,那样会导致string_view引用的内容也失效。

这样的代码是有问题的,因为出了作用域之后,string_view引用的内容已经失效了。

C++17中的structured bindings

基本用法

structured bindings让我们能通过tuple,std::pair或是没有静态数据成员的结构体来初始化变量。在C++17之前如果要解包一个结构体时,我们需要借助tie,像这样:

现在有了C++17的structured bindings,我们可以很方便地解包tuple了,像这样。

遍历map的时候也不用像以前一样写pair然后it->first, it->second了,而是用更加简洁的方式,像这样:

更酷的是你可以用来解包一个结构体。

需要注意的是,你必须提供和结构体或tuple字段个数相同的变量,否则会出现编译错误,而且你不能像std::ignore一样忽略某些字段,必须全部解包出来。解包的时候你可以选择引用或拷贝,auto&就是引用方式解包。

特殊用法

我们可以借助这个特性来做一点有趣的事,比如把一个结构体变成一个tuple。

实现的思路很巧妙,先判断某个类型是否由指定个数的参数构造,is_braces_constructible<type, any_type>{}就是判断type是否能由一个参数构造;接着通过constexpr if在编译期选择有效的分支,其它的分支将会被丢弃;最后通过structured bindings来获取结构体字段的值,并将这些值重新构造一个tuple,这样就可以实现将结构体转换为tuple了。上面的例子中由于有四个字段所以会走第一个分支。需要注意的是,这个方法只是一个示例,还没解决构造函数被delete的问题,还有隐式转换的问题,如果你想看更完整的解决方案可以看这里:http://playfulprogramming.blogspot.hk/2016/12/serializing-structs-with-c17-structured.html。

structured bindings具备解包结构体的能力,虽然限制条件也不少,我相信它的用法值得深入探索。

C++17中的constexpr

constexpr if

constexpr标记一个表达式或一个函数的返回结果是编译期常量,它保证函数会在编译期执行。相比模版来说,实现编译期循环或递归,C++17中的constexpr if会让代码变得更简洁易懂。比如实现一个编译期整数加法:

C++17之前你可能需要像上面这样写,但是现在你可以写更简洁的代码了

当然,你也可以用C++17的fold expression

constexpr还可以用来消除enable_if了,对于讨厌写一长串enable_if的人来说会非常开心。比如我需要根据类型来选择函数的时候:

经常不得不分开几个函数来写,还需要写长长的enable_if,比较繁琐,通过if constexpr可以消除enable_if了。

constexpr if让C++的模版具备if-else if-else功能了,是不是很酷,C++程序员的好日子来了:)

不过需要注意的是这种写法是有问题的哦。

这个代码把else去掉了,当输入如果是非数字类型的时候代码可以编译过,以为if constexpr在模版实例化的时候会丢弃不满足条件的部分,因此函数体中的前两行代码将失效,只有最后一句有效。当输入的为数字的时候就会产生编译错误了,因为if constexpr满足条件了,这时候就会有两个return了,就会导致编译错误。

constexpr if还可以用来替换#ifdef宏,看下面的例子

代码变得更清爽了,再也不需要像以前一样写#ifdef那样难看的代码块了。

constexpr lambda

constexpr lambda其实很简单,它的意思就是可以在constexpr 函数中用lambda表达式了,这在C++17之前是不允许的。这样使用constexpr函数和普通函数没多大区别了,使用起来非常舒服。下面是constexpr lambda的例子:

constexpr if和constexpr lambda是C++17提供的非常棒的特性,enjoy it.

iguana支持了C++17

iguana做了更新,增加了C++17的版本,之前C++14的版本并不受影响。

如何使用

如何使用C++17版本的iguana呢?很简单。

  1. 需要支持C++17的编译器:gcc7.1, clang4.0,由于vs2017只支持了很少的C++17特性,因此在vs2017上用不了C++17版本的iguana.

  2. 引用C++17版本的头文件,如include “json17.hpp”即可。

  3. CMakelists.txt中设置C++17的flag。

用到了哪些C++17的新特性?

主要用到了C++17的constexpr:constexpr if, constexpr lambda; fold expression, string_view, inline variable, nested namespace.

C++17用起来很舒服,enjoy it.

欢迎大家使用基于编译期反射的序列化引擎iguana

C++17中的Fold Expression

C++11中增加了一个新特性可变模版参数(variadic template),它可以接受任意个模版参数在参数包中,参数包是三个点…,它不能直接展开,需要通过一些特殊的方法才能展开,导致在使用的时候有点难度。现在C++17解决了这个问题,让参数包的展开变得容易了,Fold expression就是方便展开参数包的。

fold expression的语义

fold expression有4种语义:

其中pack代表变参,比如args,op代表操作符,fold expression支持32中操作符:

unary right fold的含义:

fold (E op …) 意味着 E1 op (… op (EN-1 op EN)).
顾名思义是从右边开始fold,我们来看一个具体的例子:

right fold的过程是这样的:(1+(2+(3+4))),从右边开始fold。

unary left fold的含义

fold (… op E) 意味着 ((E1 op E2) op …) op EN.

对于+这种满足交换律的操作符来说left fold和right fold是一样的,比如上面的例子你也可以写成left fold。

对于不满足交换律的操作符来说就要注意了,比如减法。

这次right fold和left fold的结果就不一样。

binary fold

Binary right fold (E op … op I) 意味着 E1 op (… op (EN-1 op (EN op I)))

Binary left fold (I op … op E) 意味着 (((I op E1) op E2) op …) op E2

二元fold的语义和一元fold的语义是相同的,看一个二元操作符的例子:

相信通过这个例子大家应该对C++17的fold expression有了基本的了解。

comma fold

在C++17之前,我们经常使用逗号表达式和std::initializer_list来将变参一个个传入一个函数。比如像下面这个例子:

这种写法比较繁琐,用fold expression就会变得很简单了。

这是right fold,你也可以写成left fold,对于comma来说两种写法是一样的,参数都是从左至右传入print_arg函数。

fold expression操作符的默认值

需要注意的是fold expression有三个操作符有默认值:

测试一下默认值:

通过这些例子可以看到fold expression确实简化了可变模版参数的展开,用起来更简单更灵活,可以基于fold expression写出更多优雅的代码,fold expression也增强了变参的威力。

Copy Protected by Chetan's WP-Copyprotect.