Title: Example Code Snippet for C++11/14/17 New Features
本文地址:http://blog.csdn.net/madongchunqiu/article/details/93721118
好多年没使用c++了,不经意间都快到C++20了。期间虽然学习了一些C++11/14/17的新功能,但是到底没有实践,忘起来也特别的快。即使买了 Effective Modern C++ 的影印版,也一直都没看。昨天想再回顾一下,网上搜到的文章都是罗列新语言特性1、2、3,贴心一点的会为每个新特性配一段代码,只是每次看一段新代码,自己都要经历一个"重启"的过程,感觉会消耗过多的发迹线,于是我尝试着写一段代码,尽量囊括所有新的语言特性。
我对这段代码的要求如下:
- 不定义新的 struct/class (用std::pair来代替)
- 不定义新的 function
- 用STL
- 用双重循环打印一个数字三角形
下面所有的工作都是在 wandbox.org 完成的。打开网页就能编辑/编译,并可以选用c++03/14/17的编译器,可以说是非常贴心了。
Photo by Émile Perron on Unsplash
C++03 的代码
作为比较,先看看代码的原始形态
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
int main()
{
vector<pair<int, string> > v;
v.push_back(pair<int, string>(1, "a"));
v.push_back(pair<int, string>(2, "b"));
v.push_back(pair<int, string>(3, "c"));
v.push_back(pair<int, string>(4, "d"));
v.push_back(pair<int, string>(5, "e"));
for (vector<pair<int, string> >::iterator it = v.begin(); it < v.end(); it++) {
for (vector<pair<int, string> >::iterator it2 = v.begin(); it2 <= it; it2++) {
cout << "(" << it2->first << "," << it2->second << ") ";
}
cout << endl;
}
}
上面这段代码输出是:
(1,a)
(1,a) (2,b)
(1,a) (2,b) (3,c)
(1,a) (2,b) (3,c) (4,d)
(1,a) (2,b) (3,c) (4,d) (5,e)
C++11/14的代码
注意:
- 下面的这段代码精华在注释
- 搜索[c++11]查看具体的语言特性
- 尽量做到每一行都有用
#include <iostream>
#include <vector>
#include <algorithm> // std::find
#include <memory> // std::shared_ptr
using namespace std;
int main(void)
{
using T = pair<int, string>; // for clearer code demo
vector<T> v = {{1, "a"}, {2, "b"}, {3, "c"}, {4, "d"}, {5, "e"}}; // [C++11]Bracketed copy-list-initialization // NO-Range-Operator
auto p(make_shared<T>(6, "f")); // [C++11]shared_ptr: use 'make_shared' instead of 'new'
v.push_back(move(*p)); // only demo, don't do like this. [C++11]Move semantic, check the effect in the last line // Note: std containers already impleted move sematics
for(auto& m: v) { // [C++11]Type Deduction (auto), [C++11]Range Based For-Loop // NO-(index, value)-pair-iteration
for_each(begin(v), find(v.begin(), v.end(), m)+1, [&m](auto n) { // generic begin(), [C++11]Lamda
if (m.first == n.first) m.second = m.second + to_string(m.first); // verify m is a reference
cout << "(" << m.first << "," << m.second << ")-(" << n.first << "," << n.second << ") "; // NO-String-Interpolation...
});
cout << endl;
}
cout << p->first << "-" << p->second << endl; // check the move effect. results based on compiler & flags
p = nullptr; // use nullptr instead of NULL
return 0;
}
上面这段代码输出是:
(1,a1)-(1,a)
(2,b)-(1,a1) (2,b2)-(2,b)
(3,c)-(1,a1) (3,c)-(2,b2) (3,c3)-(3,c)
(4,d)-(1,a1) (4,d)-(2,b2) (4,d)-(3,c3) (4,d4)-(4,d)
(5,e)-(1,a1) (5,e)-(2,b2) (5,e)-(3,c3) (5,e)-(4,d4) (5,e5)-(5,e)
(6,f)-(1,a1) (6,f)-(2,b2) (6,f)-(3,c3) (6,f)-(4,d4) (6,f)-(5,e5) (6,f6)-(6,f)
6-
从输出结果可以看出:
- m 是 pass by reference, n 是 pass by value,所以每一行的最后一个输出(即,满足条件m.first == n.first 时),n 的 string 没有受到影响
- std::move 将原始数据的 string 部分给移走了,所以最后一行输出为空
C++17的代码
重要的仅有一行,见注释
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main(void)
{
vector<pair<int, string>> v = {{1, "a"}, {2, "b"}, {3, "c"}, {4, "d"}, {5, "e"}};
for(auto& m: v) {
for_each(begin(v), find(v.begin(), v.end(), m)+1, [&m](auto n) {
if (auto &[p, q] = m; p == n.first) q = q + to_string(p); // [C++17]structured binding, [C++17]Init statements for if/switch
cout << "(" << m.first << "," << m.second << ")-(" << n.first << "," << n.second << ") ";
});
cout << endl;
}
return 0;
}
上面这段代码输出是:
(1,a1)-(1,a)
(2,b)-(1,a1) (2,b2)-(2,b)
(3,c)-(1,a1) (3,c)-(2,b2) (3,c3)-(3,c)
(4,d)-(1,a1) (4,d)-(2,b2) (4,d)-(3,c3) (4,d4)-(4,d)
(5,e)-(1,a1) (5,e)-(2,b2) (5,e)-(3,c3) (5,e)-(4,d4) (5,e5)-(5,e)
一行代码展示两个特性,不错不错。
总结
近来用 Swift 比较多,因此昨天写这些代码时难免有些比较
- C/C++ 效率总是第一位的,很高兴看到 move semantics 继续在这方面努力。近来看帖子似乎不少人觉得算力过剩,程序只要写起来爽,运行慢点无所谓。觉得算力过剩的人,真怀疑是不是学计算机的;至于写起来爽和运行快,这是不冲突的,需要的是编译器的努力和语言特性的改进。这正是C++11/14/17在做的
- 在上面C++11/14的代码中,有几个注释以 NO- 开头的,是 Swift 中有的一些语法特性,很好用,但 C++ 现在还没有
- auto 似乎的确有点多,都成段子了。而 Swift 也是强调 Type Deduction 的,代码看起来似乎没那么刺眼
附C++黑话
- Rule of 5 (创建类时需要考虑的): Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership.
- RAII (资源分配时需要考虑的): Resource Acquisition Is Initialization, is a C++ programming technique which binds the life cycle of a resource that must be acquired before use to the lifetime of an object.
- RTTI: Run-Time Type Identification, refers to the ability of the system to report on the dynamic type of an object and to provide information about that type at runtime