- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
结构化绑定允许你用一个对象的元素或成员同时实例化多个实体。 结构化绑定允许你在声明变量的同时解构一个复合类型的数据结构(如 结构体,std::tuple, std::pair, 或者 std::array)。这样可以方便地获取多个值,而不需要显式地调用 std::tie() 或者 .get() 方法.
使用结构化绑定,能大大提升代码的可读性:
#include <iostream>
#include <string>
#include <unordered_map>
int main() {
std::unordered_map<std::string,std::string> mymap;
mymap.emplace("k1","v1");
mymap.emplace("k2","v2");
mymap.emplace("k2","v3");
for (const auto& elem : mymap)
std::cout << "old: " << elem.first << " : " << elem.second << std::endl;
for (const auto& [key,value] : mymap)
std::cout << " new: " << "key: " << key << ", value: " << value << std::endl;
return 0;
}
### 细说结构化绑定
为了理解结构化绑定,必须意识到这里面其实有一个隐藏的匿名对象。结构化绑定时引入的新变量名其实都指向这个匿名对象的成员/元素。
绑定到一个匿名实体
如下初始化的精确行为:
struct MyStruct {
int i = 0;
std::string s;
};
MyStruct ms;
auto [u, v] = ms;
等价于我们用 ms初始化了一个新的实体 e,并且让结构化绑定中的 u和 v变成 e的成员的别名,类似于如下定义:
auto e = ms;
aliasname u = e.i;
aliasname v = e.s;
这意味着 u和 v仅仅是 ms的一份本地拷贝的成员的别名。然而,我们没有为 e声明一个名称,因此我们不能直接访问这个匿名对象。注意 u和 v并不是 e.i和 e.s的引用(而是它们的别名)。decltype(u)的结果是成员 i的类型,declytpe(v)的结果是成员 s的类型。因此:
std::cout << u << ' ' << v << '\n';
会打印出 e.i和 e.s(分别是 ms.i和 ms.s的拷贝)。
e的生命周期和结构化绑定的生命周期相同,当结构化绑定离开作用域时 e也会被自动销毁。另外,除非使用了引用,否则修改用于初始化的变量并不会影响结构化绑定引入的变量(反过来也一样)
### 示例代码
```cpp
#include <iostream>
#include <tuple>
#include <vector>
#include <string>
#include <map>
#include <unordered_map>
struct MyStruct {
int num {1};
std::string str {"test"};
};
int main() {
// 使用结构化绑定从 tuple 中提取值
std::tuple<int, double, std::string> data = std::make_tuple(1, 3.14, "hello");
auto [a, b, c] = data;
std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
//value
MyStruct my_struc;
auto [num,str] = my_struc;
std::cout << "num: " << num << ", str: " << str << std::endl;
my_struc.num = 2;
str = "test2";
std::cout << "num: " << num << ", str: " << str << std::endl;
//ref
MyStruct my_struc2 {2,"test2"};
const auto& [num2,str2] = my_struc2;
std::cout << "num2: " << num2 << ", str2: " << str2 << std::endl;
my_struc2.num = 4;
std::cout << "num2: " << num2 << ", str2: " << str2 << std::endl;
return 0;
}
### 总结
理论上讲,结构化绑定适用于任何有 public数据成员的结构体、C 风格数组和“类似元组 (tuple-like)的对
象”:
• 对于所有非静态数据成员都是 public的结构体和类,你可以把每一个成员绑定到一个新的变量名上。
• 对于原生数组,你可以把数组的每一个元素都绑定到新的变量名上。
• 对于任何类型,你可以使用 tuple-like API 来绑定新的名称,无论这套 API 是如何定义“元素”的。对于一个
类型 type这套 API 需要如下的组件:
– std::tuple_size<type>::value要返回元素的数量。
– std::tuple_element<idx, type>::type 要返回第 idx个元素的类型。
– 一个全局或成员函数 get<idx>()要返回第 idx个元素的值。
标准库类型 std::pair<>、std::tuple<>、std::array<> 就是提供了这些 API 的例子。如果结构体和类提供了 tuple-like API,那么将会使用这些 API 进行绑定,而不是直接绑定数据成员。
## 2. 拷贝消除(Copy Elision)
从技术上讲,C++17引入了一个新的规则:当以值传递或返回一个临时对象的时候必须省略对该临时对象的拷贝。
从效果上讲,我们实际上是传递了一个未实质化的对象 (unmaterialized object)。
自从第一次标准开始,C++就允许在某些情况下省略 (elision)拷贝操作,即使这么做可能会影响程序的运行结果(例如,拷贝构造函数里的一条打印语句可能不会再执行)。当用临时对象初始化一个新对象时就很容易出现这种情况,尤其是当一个函数以值传递或返回临时对象的时候。例如:
```cpp
#include <iostream>
#include <tuple>
#include <vector>
#include <string>
#include <map>
#include <complex>
class MyClass
{
public:
// 没有拷贝/移动构造函数的定义
MyClass(const MyClass&) = delete;
MyClass(MyClass&&) = delete;
};
MyClass bar() {
return MyClass{}; // 返回临时对象
}
int main() {
MyClass x = bar(); // 使用返回的临时对象初始化x
return 0;
}
上面的代码用c++14会报错,c++17已经不报错了
然而,注意其他可选的省略拷贝的场景仍然是可选的,这些场景中仍然需要一个拷贝或者移动构造函数。例如:
MyClass foo()
{
MyClass obj;
...
return obj; // 仍 然 需 要 拷 贝/移 动 构 造 函 数 的 支 持
}
这里,foo()中有一个具名的变量 obj (当使用它时它是左值 (lvalue))。因此,具名返回值优化 (named returnvalue optimization)(NRVO) 会生效,然而该优化仍然需要拷贝/移动支持。当 obj是形参的时候也会出现这种情况:
MyClass bar(MyClass obj) // 传 递 临 时 变 量 时 会 省 略 拷 贝
{
...
return obj; // 仍 然 需 要 拷 贝/移 动 支 持
}
当传递一个临时变量(也就是纯右值 (prvalue))作为实参时不再需要拷贝/移动,但如果返回这个参数的话仍然需要拷贝/ 移动支持因为返回的对象是具名的。
### 作用
这个特性的一个显而易见的作用就是减少拷贝会带来更好的性能。尽管很多主流编译器之前就已经进行了这种优化,但现在这一行为有了标准的保证。尽管移动语义能显著的减少拷贝开销,但如果直接不拷贝还是能带来很大的性能提升(例如当对象有很多基本类型成员时移动语义还是要拷贝每个成员)。另外这个特性可以减少输出参数的使用,转而直接返回一个值(前提是这个值直接在返回语句里创建)。另一个作用是可以定义一个总是可以工作的工厂函数,因为现在它甚至可以返回不允许拷贝或移动的对象。
例如,考虑如下泛型工厂函数:
```cpp
#include <iostream>
#include <tuple>
#include <vector>
#include <string>
#include <map>
#include <complex>
#include <utility>
#include <memory>
#include <atomic>
template <typename T, typename... Args>
T create(Args&&... args)
{
return T{std::forward<Args>(args)...};
}
int main() {
int i = create<int>(42);
std::unique_ptr<int> up = create<std::unique_ptr<int>>(new int{42});
std::atomic<int> ai = create<std::atomic<int>>(42);
std::cout << "ai: " << ai << std::endl;
return 0;
}
最后此篇关于掌握C++17:结构化绑定与拷贝消除的妙用的文章就讲到这里了,如果你想了解更多关于掌握C++17:结构化绑定与拷贝消除的妙用的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我编写了一些运行速度很慢的 VBA 代码。我的代码中有一系列不同的循环。我知道循环并不总是处理数据的最有效方式,所以我认为它们是问题所在。我需要有关如何更改或消除循环的想法,以便加快代码的运行时间。
我目前有一个网址:http://testsite.local/search/?q=findme一旦有人查询,我的搜索页面。我使用 mod_rewrite 重写了一些页面,想知道是否可以将其变成一个不错
有人可以帮助我执行一个查询,其中查询的重复元素被删除 Select * from table where APPNAME = 'Ap1' or APPNAME= 'Ap2' 使用 DISTINCT 的
我正在尝试在 ubuntu 上使用以下命令在一个文件夹中查找文件并通过 FFmpeg 提供并输出到另一个文件夹。问题是当它处理输出路径和文件名时,它添加了一个 .像这样的路径:/conversions
这个问题在这里已经有了答案: How can I remove all duplicates so that NONE are left in a data frame? (3 个答案) 关闭 1
我想证明以下定理: Theorem Frobenius (A: Set) (q: Prop) (p: A -> Prop) : (q \/ forall x : A, p x) -> (foral
我有一个 PHP 脚本,它只需要一些数据,将其分隔为制表符分隔格式,将其保存为 .xls 文件,然后为用户提供下载链接。 大多数情况下运行良好,但有些人正在获取导出的 .xls 文件的缓存版本。 我想
我有一个看起来有点像这个可重现代码的数据框,我想删除每列的异常值(在我们的例子中,数据点低于或高于平均值 2.5 个标准偏差)而不删除整个主题/行。 Subj mn + sd * 2.5) | (x
我正在尝试编写一个实现 fmap 的演示。在 Haskell 中与 continuation ,我的代码如下所示: #include #include template using Callba
在此 HighCharts例如,如何消除 xaxis 开始位置与 Jan 的刻度位置之间的差距。 http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-s
重现步骤: 将TPanel添加到新的VCL表单并设置Align = alClient。 将 TSpeedButton 添加到面板,并将一些 bmp 图像分配给 Glyph 属性。 (可选,但更清晰:F
我得到一个 JavaScript 数组,其中包含一定数量(未指定)的各种元素(字符串、数字、 bool 值)。我需要消除字符串和 bool 值。我应该如何处理它?我应该使用typeof吗? 最佳答案
我正在寻找一个公式,可以消除字符串中空格后的空格和无用字符。 我的第一 react 是执行以下操作:=LEFT(A1,FIND("",A1)) 它适用于所有有空格的情况 但是如果单元格中没有空格,我的
我有以下问题:我正在尝试编写一个 Javascript 游戏,并且 Angular 色由箭头键控制。 问题是,当一个人按住按键时,在触发第一个按键和重复的按键之间存在短暂的延迟。 另外,当按下“向右箭
让我们考虑一个集合的集合,以及需要在管道内对内部集合的每个元素执行的操作。 为了简单起见,让它成为一个数组数组,操作简单的打印到屏幕上。为了表达我的问题,让我们还有一个元素不是集合的数组: $Arra
跟进this question关于包含源文件。我包括一个 Chapel 模块,其中包含一个名为 classes.chpl 的文件。 ,但我当前的项目也有一个 classes.chpl 。正确的消歧模式
我想知道如何在英特尔语法中的某些指令中区分标签名称和寄存器名称。例如,call rdx通常意味着间接跳转,但是如果我们在同一个汇编文件中有一个标签rdx怎么办?我相信它可以被解释为直接跳转到 rdx
据我了解,Chrome 会异步运行整个程序,这会导致我的扩展程序在单击后大约 2 秒后打开。有没有办法强制扩展程序显示带有“正在加载”消息的 html 页面,然后完成加载 javascript 并用内
我正在将 CSV 加载到 sqlite 数据库,如下所示: sqlite3 /path/to/output.db /dev/null 或者,您可以自己生成 SQL 命令,以便可以使用 INSERT 或
我的 .cabal 文件的许多节中经常有类似的属性。例如 Library x ... ghc-options: -O2 -Wall -fno-warn-missing-s
我是一名优秀的程序员,十分优秀!