C++17 部分实用特性讲解

    2017年12月6日,C++17 标准正式发布!鉴于目前国内介绍 C++17 新特性的资料不多,而且就算提到了 C++17,也无非以下两类:一类只是提了一下有哪些新特性,而没详细讲解用法;另外一类就是在畅想 concept , module 等本来期望着能进 C++17, 最终却无缘入围的功能。因此,本人决定从国外的视频中搬运一些详解 C++17 新特性的学习资料,供大家围观。


    本文部分内容参考了 Bryce Lelbach 主讲的视频 C++17 Features -- An Introduction to C++17 Via Inspiring Examples ,这位老兄特别健谈,一个人连续讲解了一个半小时没有休息,向他致敬!另外,由于本人最近手头事有点多,所以边写边更,速度慢请见谅!理解有误的地方也请各位同仁批评指正!


1. 构造函数的模板推导 (Class template deduction)

    直接上代码,大家先有个直观感受


#include 
#include 

using namespace std;

int main()
{
	pair p(3, 3.5); //now
	cout << p.first << "  " << p.second << endl;


	pair  p2(3, 3.5); //before
	cout << p2.first << "  " << p2.second << endl;


	pair p3(3.5,"abc"); //now, 自动推导为 pair 类型
	cout << p3.first << "  " << p3.second << endl;

	/*
	p3.second.length(); //提示出错, gcc 7.1 的提示信息为:
		[Error] request for member 'length' in 'p3.std::pair::second', which is of non-class type 'const char*'
	*/

	return 0;
}

    可以看到,在以前,必须手动指定 pair 的模板参数。现在呢,可以通过你传进去的构造参数的类型自动推导生成的 pair 的类型了。不过要当心了,看那个 p3 ,第二个模板参数推导出的类型是 const char* ,不是 string !

    当然,这种自动模板类型推导也适用于 vector , tuple (元组) 等其他容器,例如:


#include 
#include 

using namespace std;

int main()
{
	vector v = {1.2, 5.6, 8.7};

	for(const auto & ele : v) {
		cout << ele << endl;
	}

	return 0;
}

2. 结构化绑定 (Structured bindings)

    记得以前学 C 语言时,要想让一个函数能返回两个值的话,用的是参数列表里传指针的形式;或者你自己再定义一个结构体也行。后来么,有了 C++ 里的 pair ,虽然理论上可以用这个返回两个参数,但是实际操作中用的还是不多。

    当然了么,人心是贪婪的。既然能返回两个值,我肯定就会想着如何返回三个值 ~~~ 貌似用 tuple 里的 get<0>()   get<1>()  get<2>()  太烦了点。

    而结构化绑定就是这样一种利器,能帮你更优雅的实现这些想法。

#include 
#include 
#include 

using namespace std;

pair get_record()
{
	return pair(0, "Jebediah");
}

auto get_record2() //这个函数的返回值类型声明利用了 C++14 中自动推导返回类型的特性, 等价于下面一个版本
{
	return tuple(1, "Bill", "engineer");
}

/*
tuple get_record2()
{
	return tuple(1, "Bill", "engineer");
}
*/

int main()
{

	auto [id, name] = get_record();
	cout << id << "   " << name << endl;

	auto [id2, name2, job2] = get_record2();
	//注意,这里要是不写成 id2, name2, ... 的话,就和上面的变量名起冲突了
	cout << id2 << "   " << name2 << "   " << job2 << endl;

	return 0;
}


    最近 map 这个容器用的挺多的,干脆来介绍一下它在 map 中的一些应用:


#include 
#include 
#include 

using namespace std;


int main()
{
	map m = {
		{0, "Jebediah"},
		{1, "Bob"},
		{2, "Bill"},
	}; //这边貌似就不能用 map m = { {0, "Jebediah"}, ...}; 这种方式做自动类型推导了, 应该是标准还没发展到那么智能吧
	//g++ 7.1 报错:	[Error] no matching function for call to 'map()'


	/* now in C++17 */
	for(const auto & [key, value] : m) {
		cout << key << "   " << value << endl;
	}
	cout << endl;



	/* before */
	for(const pair & result : m) {
		cout << result.first << "   " << result.second << endl;
	}
	cout << endl;



	/*
	  当然,如果你觉得用 first, second 去取 key, value 很丑陋的话,
	  你也可以借助 .* 运算符给他们取个别名,借机骚一波
	 */
	typedef pair find;
	const int find:: * key = &find::first;
	string find:: * value = &find::second;

	for(const pair & result : m) {
		cout << result.*key << "   " << result.*value << endl;
	}

	/* 不过鉴于用 .* 运算符写起来太晦涩了,所以第三种写法就算了吧 */


	return 0;
}


    你可能要说了,C++11 里的 tie 也能完成这些功能啊。不好意思,Bryce 同志在他的幻灯片里罗列了一大堆 tie 的劣处:


/* 1 */
std::tuple t = // ...

double x, y, z;

std::tie(x, y, z) = t; // correct 


/* 2 */
std::tuple t = // ...

double x, y, z;

std::tie(y, y, z) = t; // ^ UH-OH : no warning for repeated names.


/* 3 */
std::tuple t = // ...

double &x, &y, &z; // COMPILE ERROR
				   // Uninitialized refs.

std::tie(x, y, z) = t;


/* 4 */
std::tuple t = // ...

double const x, y, z;

std::tie(x, y, z) = t; // COMPILE ERROR
					   // Assignment to const.

    而关于哪些类能用上结构化绑定的方式,以及怎么用,我不高兴打字了,直接上图吧:


C++17 部分实用特性讲解_第1张图片


C++17 部分实用特性讲解_第2张图片


3. 选择语句中的初始化器 (Selection Statements with Initializers)

    顾名思义,就是现在能在 if 语句中做一些初始化工作了,上用法:


Syntax
Equivalent To
if (init; cond)
    statement1;
else
    statement2;
{
    init;
    if (cond)
        statement1;
    else
        statement2;
}


    上样例:

#include 
#include 
#include 

using namespace std;

int main()
{
	map m = {
		{0, "Jebediah"},
		{1, "Bob"},
		{2, "Bill"},
	};

	/* now */
	if(auto it = m.find(1); it != m.end()) {
		auto [key, value] = *it;
		cout << value << endl;
	}

	/* before */
	auto it = m.find(1);
	if(it != m.end()) {
		cout << it->second << endl;
	}

	return 0;
}



    更多特性,未完待续。。。



你可能感兴趣的:(C++17 部分实用特性讲解)