stl 的 map 容器很多读者应该都很熟悉map 容器提供了一个insert方法我们用该方法向 map 中插入元素但是应该很少有人记得insert方法的返回值是什么类型让我们来看一下 C98/03 提供的insert方法的签名std::pairiterator,bool insert( const value_type value );这里我们仅关心其返回值这个返回值是一个std::pairT1, T2类型由于 map 中的元素的 key 不允许重复所以如果 insert 方法调用成功T1 是被成功插入到 map 中的元素的迭代器T2 的类型为 bool此时其值为 true表示插入成功如果 insert 由于 key 重复T1 是造成 insert 插入失败、已经存在于 map 中的元素的迭代器此时 T2 的值为 false表示插入失败。在 C98/03 标准中我们可以使用std::pairT1, T2的first和second属性来分别引用 T1 和 T2 的值。如下面的我们熟悉的代码所示#include iostream #include string #include map int main() { std::mapstd::string, int cities; cities[beijing] 0; cities[shanghai] 1; cities[shenzhen] 2; cities[guangzhou] 3; //for (const auto [key, value] : m) //{ // std::cout key : value std::endl; //} //这一行在 C11 之前写法实在太麻烦了 //std::pairstd::mapstd::string, int::iterator, int insertResult cities.insert(std::pairstd::string, int(shanghai, 2)); //C11中我们写成 auto insertResult cities.insert(std::pairstd::string, int(shanghai, 2)); std::cout Is insertion successful ? (insertResult.second ? true : false) , element key: insertResult.first-first , value: insertResult.first-second std::endl; return 0; }代码19行实在太麻烦了我们使用 auto 关键字让编译器自动推导类型。std::pair一般只能表示两个元素C11 标准中引入了std::tuple类型有了这个类型我们就可以放任意个元素了原来需要定义成结构体的 POD 对象我们可以直接使用std::tuple表示例如下面表示用户信息的结构体struct UserInfo { std::string username; std::string password; int gender; int age; std::string address; }; int main() { UserInfo userInfo { Tom, 123456, 0, 25, Pudong Street }; std::string username userInfo.username; std::string password userInfo.password; int gender userInfo.gender; int age userInfo.age; std::string address userInfo.address; return 0; }我们不再需要定义 struct UserInfo 这样的对象可以直接使用std::tuple表示int main() { std::tuplestd::string, std::string, int, int, std::string userInfo(Tom, 123456, 0, 25, Pudong Street); std::string username std::get0(userInfo); std::string password std::get1(userInfo); int gender std::get2(userInfo); int age std::get3(userInfo); std::string address std::get4(userInfo); return 0; }从std::tuple中获取对应位置的元素我们使用std::getN其中 N 是元素的序号从 0 开始。与定义结构体相比通过std::pair的first和second还是std::tuple的std::getN方法来获取元素子属性这些代码都是非常难以维护的其根本原因是first和second这样的命名不能做到见名知意。C17 引入的结构化绑定Structured Binding 将我们从这类代码中解放出来。结构化绑定使用语法如下auto [a, b, c, ...] expression; auto [a, b, c, ...] { expression }; auto [a, b, c, ...] ( expression );右边的expression可以是一个函数调用、花括号表达式或者支持结构化绑定的某个类型的变量。例如//形式1 auto [iterator, inserted] someMap.insert(...); //形式2 double myArray[3] { 1.0, 2.0, 3.0 }; auto [a, b, c] myArray; //形式3 struct Point { double x; double y; }; Point myPoint(10.0, 20.0); auto [myX, myY] myPoint;这样我们可以给用于绑定到目标的变量名语法中的a、b、c起一个有意义的名字。需要注意的是绑定名称a、b、c是绑定目标的一份拷贝当绑定类型不是基础数据类型时如果你的本意不是想要得到绑定目标的副本为了避免拷贝带来的不必要开销建议使用引用如果不需要修改绑定目标建议使用 const 引用。示例如下double myArray[3] { 1.0, 2.0, 3.0 }; auto [a, b, c] myArray; //形式3 struct Point { double x; double y; }; Point myPoint(10.0, 20.0); const auto [myX, myY] myPoint;结构化绑定Structured Binding 是 C17 引入的一个非常好用的语法特性。有了这种语法在遍历像 map 这样的容器时我们可以使用更简洁和清晰的代码去遍历这些容器了std::mapstd::string, int cities; cities[beijing] 0; cities[shanghai] 1; cities[shenzhen] 2; cities[guangzhou] 3; for (const auto [cityName, cityNumber] : cities) { std::cout cityName : cityNumber std::endl; }上述代码中cityName和cityNumber可以更好地反映出这个 map 容器的元素内容。我们再来看一个例子某 WebSocket 网络库https://github.com/uNetworking/uWebSockets中有如下代码std::pairint, bool uncork(const char *src nullptr, int length 0, bool optionally false) { LoopData *loopData getLoopData(); if (loopData-corkedSocket this) { loopData-corkedSocket nullptr; if (loopData-corkOffset) { /* Corked data is already accounted for via its write call */ auto [written, failed] write(loopData-corkBuffer, loopData-corkOffset, false, length); loopData-corkOffset 0; if (failed) { /* We do not need to care for buffering here, write does that */ return {0, true}; } } /* We should only return with new writes, not things written to cork already */ return write(src, length, optionally, 0); } else { /* We are not even corked! */ return {0, false}; } }代码的第9行write函数返回类型是std::pairint, bool被绑定到[written, failed]这两个变量中去。前者在写入成功的情况下表示实际写入的字节数后者表示是否写入成功。std::pairint, bool write(const char *src, int length, bool optionally false, int nextLength 0) { //具体实现省略... }结构化绑定的限制结构化绑定不能使用constexpr修饰或被申明为 static例如//正常编译 auto [first, second] std::pairint, int(1, 2); //无法编译通过 //constexpr auto [first, second] std::pairint, int(1, 2); //无法编译通过 //static auto [first, second] std::pairint, int(1, 2);有些编译器也不支持在 lamda 表达式捕获列表中使用结构化绑定语法。关于 lamda 表达式我们将在下面的小节中介绍。
结构化绑定
发布时间:2026/7/2 11:07:33
stl 的 map 容器很多读者应该都很熟悉map 容器提供了一个insert方法我们用该方法向 map 中插入元素但是应该很少有人记得insert方法的返回值是什么类型让我们来看一下 C98/03 提供的insert方法的签名std::pairiterator,bool insert( const value_type value );这里我们仅关心其返回值这个返回值是一个std::pairT1, T2类型由于 map 中的元素的 key 不允许重复所以如果 insert 方法调用成功T1 是被成功插入到 map 中的元素的迭代器T2 的类型为 bool此时其值为 true表示插入成功如果 insert 由于 key 重复T1 是造成 insert 插入失败、已经存在于 map 中的元素的迭代器此时 T2 的值为 false表示插入失败。在 C98/03 标准中我们可以使用std::pairT1, T2的first和second属性来分别引用 T1 和 T2 的值。如下面的我们熟悉的代码所示#include iostream #include string #include map int main() { std::mapstd::string, int cities; cities[beijing] 0; cities[shanghai] 1; cities[shenzhen] 2; cities[guangzhou] 3; //for (const auto [key, value] : m) //{ // std::cout key : value std::endl; //} //这一行在 C11 之前写法实在太麻烦了 //std::pairstd::mapstd::string, int::iterator, int insertResult cities.insert(std::pairstd::string, int(shanghai, 2)); //C11中我们写成 auto insertResult cities.insert(std::pairstd::string, int(shanghai, 2)); std::cout Is insertion successful ? (insertResult.second ? true : false) , element key: insertResult.first-first , value: insertResult.first-second std::endl; return 0; }代码19行实在太麻烦了我们使用 auto 关键字让编译器自动推导类型。std::pair一般只能表示两个元素C11 标准中引入了std::tuple类型有了这个类型我们就可以放任意个元素了原来需要定义成结构体的 POD 对象我们可以直接使用std::tuple表示例如下面表示用户信息的结构体struct UserInfo { std::string username; std::string password; int gender; int age; std::string address; }; int main() { UserInfo userInfo { Tom, 123456, 0, 25, Pudong Street }; std::string username userInfo.username; std::string password userInfo.password; int gender userInfo.gender; int age userInfo.age; std::string address userInfo.address; return 0; }我们不再需要定义 struct UserInfo 这样的对象可以直接使用std::tuple表示int main() { std::tuplestd::string, std::string, int, int, std::string userInfo(Tom, 123456, 0, 25, Pudong Street); std::string username std::get0(userInfo); std::string password std::get1(userInfo); int gender std::get2(userInfo); int age std::get3(userInfo); std::string address std::get4(userInfo); return 0; }从std::tuple中获取对应位置的元素我们使用std::getN其中 N 是元素的序号从 0 开始。与定义结构体相比通过std::pair的first和second还是std::tuple的std::getN方法来获取元素子属性这些代码都是非常难以维护的其根本原因是first和second这样的命名不能做到见名知意。C17 引入的结构化绑定Structured Binding 将我们从这类代码中解放出来。结构化绑定使用语法如下auto [a, b, c, ...] expression; auto [a, b, c, ...] { expression }; auto [a, b, c, ...] ( expression );右边的expression可以是一个函数调用、花括号表达式或者支持结构化绑定的某个类型的变量。例如//形式1 auto [iterator, inserted] someMap.insert(...); //形式2 double myArray[3] { 1.0, 2.0, 3.0 }; auto [a, b, c] myArray; //形式3 struct Point { double x; double y; }; Point myPoint(10.0, 20.0); auto [myX, myY] myPoint;这样我们可以给用于绑定到目标的变量名语法中的a、b、c起一个有意义的名字。需要注意的是绑定名称a、b、c是绑定目标的一份拷贝当绑定类型不是基础数据类型时如果你的本意不是想要得到绑定目标的副本为了避免拷贝带来的不必要开销建议使用引用如果不需要修改绑定目标建议使用 const 引用。示例如下double myArray[3] { 1.0, 2.0, 3.0 }; auto [a, b, c] myArray; //形式3 struct Point { double x; double y; }; Point myPoint(10.0, 20.0); const auto [myX, myY] myPoint;结构化绑定Structured Binding 是 C17 引入的一个非常好用的语法特性。有了这种语法在遍历像 map 这样的容器时我们可以使用更简洁和清晰的代码去遍历这些容器了std::mapstd::string, int cities; cities[beijing] 0; cities[shanghai] 1; cities[shenzhen] 2; cities[guangzhou] 3; for (const auto [cityName, cityNumber] : cities) { std::cout cityName : cityNumber std::endl; }上述代码中cityName和cityNumber可以更好地反映出这个 map 容器的元素内容。我们再来看一个例子某 WebSocket 网络库https://github.com/uNetworking/uWebSockets中有如下代码std::pairint, bool uncork(const char *src nullptr, int length 0, bool optionally false) { LoopData *loopData getLoopData(); if (loopData-corkedSocket this) { loopData-corkedSocket nullptr; if (loopData-corkOffset) { /* Corked data is already accounted for via its write call */ auto [written, failed] write(loopData-corkBuffer, loopData-corkOffset, false, length); loopData-corkOffset 0; if (failed) { /* We do not need to care for buffering here, write does that */ return {0, true}; } } /* We should only return with new writes, not things written to cork already */ return write(src, length, optionally, 0); } else { /* We are not even corked! */ return {0, false}; } }代码的第9行write函数返回类型是std::pairint, bool被绑定到[written, failed]这两个变量中去。前者在写入成功的情况下表示实际写入的字节数后者表示是否写入成功。std::pairint, bool write(const char *src, int length, bool optionally false, int nextLength 0) { //具体实现省略... }结构化绑定的限制结构化绑定不能使用constexpr修饰或被申明为 static例如//正常编译 auto [first, second] std::pairint, int(1, 2); //无法编译通过 //constexpr auto [first, second] std::pairint, int(1, 2); //无法编译通过 //static auto [first, second] std::pairint, int(1, 2);有些编译器也不支持在 lamda 表达式捕获列表中使用结构化绑定语法。关于 lamda 表达式我们将在下面的小节中介绍。