1.9 C++17结构化绑定
对于std::map容器,很多读者应该都很熟悉。map容器提供了一个insert方法,用于向 map 中插入元素,但是很少有人记得 insert 方法的返回值是什么类型。让我们看一下C++98/03提供的insert方法的签名:
这里我们仅关心其返回值,这个返回值是 std::pair<T1,T2>类型。由于 map中元素的key不允许重复,所以如果 insert方法调用成功,则 T1是被成功插入 map中的元素的迭代器,T2的类型为bool,此时其值为true(表示插入成功);如果insert由于key重复,T1是造成插入失败、已经存在于 map中的元素的迭代器,则此时 T2的值为 false(表示插入失败)。
在C++98/03标准中可以使用 std::pair<T1,T2>的first和second属性分别引用 T1和T2的值。如下面的代码所示:
以上代码太繁复了,我们可以使用auto关键字让编译器自动推导类型。
std::pair一般只能表示两个元素,在C++11标准中引入了std::tuple类型,有了这个类型,我们就可以放任意数量的元素了,原来需要被定义成结构体的POD对象,我们可以直接使用std::tuple表示。例如,对于下面表示用户信息的结构体:
我们不再需要定义struct UserInfo这样的对象,可以直接使用std::tuple表示:
从std::tuple中获取对应位置的元素时,可以使用std::get<N>,其中N是元素的序号(从0开始)。
与定义结构体相比,无论是通过std::pair的first、second还是std::tuple的std::get<N>方法来获取元素子属性,这些代码都是难以维护的,其根本原因是 first和second这样的命名不能做到见名知意。
C++17引入的结构化绑定(Structured Binding)将我们从这类代码中“解放”出来。结构化绑定使用的语法如下:
右边的expression可以是一个函数调用、花括号表达式或者支持结构化绑定的某个类型的变量。例如:
这样,我们可以给用于绑定到目标的变量名(语法中的a、b、c)起一个有意义的名称。
需要注意的是,绑定名称a、b、c是绑定目标的一份拷贝,当绑定类型不是基础数据类型时,如果你的本意不是想要得到绑定目标的副本,则为了避免拷贝带来的不必要开销,建议使用引用;如果不需要修改绑定目标,则建议使用const引用。示例如下:
结构化绑定(Structured Binding)是C++17引入的一个非常好用的语法特性。有了这种语法,在遍历像map这样的容器时,我们可以使用更简洁和清晰的代码去遍历这些容器:
在以上代码中,cityName 和 cityNumber 可以更好地反映这个 map 容器的元素内容。再来看一个例子,在某WebSocket网络库中有如下代码:
在以上加粗代码行中,write函数的返回类型是std::pair<int,bool>,被绑定到written、failed这两个变量中。前者在写入成功的情况下表示实际写入的字节数,后者表示是否写入成功:
结构化绑定的限制
用于结构化绑定的变量不能使用constexpr修饰或声明为static,例如:
有些编译器也不支持在Lamda表达式捕获列表中使用结构化绑定的语法。