优秀的编程知识分享平台

网站首页 > 技术文章 正文

C++17:结构化绑定

nanyue 2025-01-08 16:19:15 技术文章 4 ℃

提案

C++17 功能特性

提案

GCC

Clang

MSVC

结构化绑定

P0217R3

7

4

19.11*


结构化绑定由 Herb Sutter、Bjarne Stroustrup、Gabriel Dos Reis 在 https://wg21.link/p0144r0中首次提出, 当时提议使用花括号而不是方括号。

最终被接受的是 Jens Maurer 发表于https://wg21.link/p0217r3的提案。

语法

  1. 属性(可选) cv-auto 引用运算符(可选) [ 标识符列表 ] = 表达式 ;
  2. 属性(可选) cv-auto 引用运算符(可选) [ 标识符列表 ] { 表达式 } ;
  3. 属性(可选) cv-auto 引用运算符(可选) [ 标识符列表 ] ( 表达式 ) ;

属性

任意数量的属性的序列

cv-auto

可有 cv 限定的 auto 类型说明符,也可以包含存储类说明符 static 或 thread_local ;在 cv 限定符中包含 volatile 是被弃用的 (C++20 起)

引用运算符

& 或 && 之一

标识符列表

此声明所引入的各标识符的逗号分隔的列表

表达式

  • 对于所有非静态数据成员都是 public的结构体和类,你可以把每一个成员绑定到一个新的变量名上
  • 对于原生数组,你可以把数组的每一个元素都绑定到新的变量名上。
  • 对于任何类型,你可以使用 tuple-like API 来绑定新的名称,无论这套 API 是如何定义“元素”的

对于一个 类型 type这套 API 需要如下的组件:

  • std::tuple_size::value要返回元素的数量
  • std::tuple_element::type 要返回第 idx个元素的类型
  • 一个全局或成员函数 get()要返回第 idx个元素的值
  • 结构化绑定其实有一个隐藏的匿名对象。结构化绑定时引入的新变量名其实都指向这个匿名对象的成员或元素
auto [u, v] = ms;
//相当于
auto e = ms;
aliasname u = e.i;
aliasname v = e.s;
//u和 v仅仅是 ms的一份本地拷贝的成员的别名
// u和 v并不是 e.i和 e.s的引用(而是它们的别名)
  • 说明符会作用在匿名实体e上,而不是结构化绑定引入的新的变量名上
alignas(16) auto [u, v] = ms; // 对 齐 匿 名 实 体e, 而 不 是u、v
  • e的生命周期和结构化绑定(方括号中的)的生命周期相同,当结构化绑定离开作用域时 e也会被自动销毁
  • 如果一个结构化绑定是引用类型,而且是对一个临时对象的引用,那么和往常一样,临时对象的生命周期会被延长到结构化绑定的生命周期
MyStruct getStruct();
...
const auto& [a, b] = getStruct();
std::cout << "a: " << a << '\n'; // OK
  • 使使用了 auto结构化绑定也不会发生类型退化 (decay是指当参数按值传递时发生的类型转换,例如原生数组会转换为指针,顶层修饰符例如 const和引用会被忽略)
struct S {
  const char x[6];
  const char y[3];
};
S s1{};
auto [a, b] = s1; // a类型是char [6],b类型是char [3]
  • 结构化绑定中声明的变量名的数量都必须和元素或数据成员的数量相同
  • 目前还不支持嵌套化的结构化绑定、联合使用结构化绑定

例子

#include <iostream>
#include <vector>
#include <tuple>

void test_array()
{
  int a[2] = {1,2};
    
  auto [x,y] = a; // 创建 e[2],复制 a 到 e,然后 x 指代 e[0],y 指代 e[1]
  auto& [xr, yr] = a; // xr 指代 a[0],yr 指代 a[1]
  std::cout << x << y << std::endl;
}

void test_tuple()
{
  float x{};
  char  y{};
  int   z{};
 
  std::tuple<float&,char&&,int> tpl(x,std::move(y),z);
  const auto& [a,b,c] = tpl;
  // a 指名指代 x 的结构化绑定;decltype(a) 为 float&
  // b 指名指代 y 的结构化绑定;decltype(b) 为 char&&
  // c 指名指代 tpl 的第 3 元素的结构化绑定;decltype(c) 为 const int
  std::cout << a << b << c << std::endl;
}

void test_class()
{
  struct MyStruct {
    int i = 0;
    std::string s;
  };

  MyStruct ms;
  auto [u, v] = ms;
  auto [u2, v2] {ms};
  auto [u3, v3] (ms);
  std::cout << u << v << std::endl;
}

int main(int argc, char** argv) {
    test_array();
    test_tuple();
    test_class();

    return 0;
}
https://wandbox.org/

惯用法

函数返回值,同时返回多个值

std::tuple<char, float, std::string> getTuple();
...
auto [a, b, c] = getTuple(); // a,b,c的 类 型 和 值 与 返 回 的tuple中 相 应 的 成 员 相 同
std::map<std::string, int> coll;
auto [pos, ok] = coll.insert({"new", 42});
if (!ok) {
  // 如 果 插 入 失 败, 用pos处 理 错 误

}

扩展

为结构化绑定提供 Tuple- Like API

Tags:

最近发表
标签列表