编译环境说明:gcc 8.1 + eclipse +windows 10

eclipse cpp默认支持c++14,做c++17开发时,需要手动进行配置。

1、关键字

1)constexpr

c++17扩展了constexpr使用范围,既可以用于if语句,也可以用于lambda表达式。

例1

template<bool ok>
constexpr void foo()
{
//在编译期间进行判断,if和else语句不生成代码
if constexpr (ok== true)
{
//当ok为true时,下面的else语句块不生成汇编代码。
cout<<"ok"<<endl;
}
else
{
//当ok为false时,上面的if语句块不生成汇编代码。
cout<<"not ok"<<endl;
}
} int main() {
foo<true>(); //输出ok,并且汇编代码中只有cout<<"ok"<<endl;这一句。
foo<false>();
return 0;
}

例2:

int main() {
constexpr auto add1 = [](int n,int m){
auto func1 = [=]{return n;};//func1 lambda表达式
auto func2 = [=]{return m;};//func2 lambad表达式
return [=]{ return func1()+func2();};
}; constexpr auto add2 = [](int n,int m){
return n+m;
}; auto add3 = [](int n,int m){
return n+m;
}; int sum1 = add1(30,40)(); //传入常量,add1在编译期计算,立即返回70.
int sum2 = add2(sum1,4); //传入非constexpr 变量,add2的constexpr失效,变成运行期lambda.
constexpr int sum3 = add3(1,2); //sum3为constexpr变量,传入常量值,add3编译期lambda。立即返回3.
int sum4 = add2(10,2);//传入常量值,add2编译期计算,立即返回12.
return 0;
}

2)static_assert

扩展static_assert用法,静态断言的显示文本可选。

static_assert(true,"");
static_assert(true); //c++17

3)auto

扩展autod的推断范围。

auto x1 = { 1, 2 }; //推断出std::initializer_list<int>类型
auto x2 = { 1, 2.0 }; //错误:类型不统一,无法推断
auto x3{ 1, 2 }; //错误:auto的聚合初始化只能一个元素
auto x4 = { 3 }; //推断出std::initializer_list<int>类型
auto x5{ 3 }; //推断出int类型

4)typename

传统用法:a)用于模板,表示模板参数为类型;b)用于声明某名字是变量。

struct A
{
typedef int Example;
}; //第一种用法,声明模板参数为类型
template<typename T>
struct B
{ }; struct C
{
//第二种用法,声明某名字为类型
typedef typename A::Example E;
}; int main() {
//第二种用法,声明某名字为类型
typename A::Example e;
return 0;
}

c++17 新特性下typename用法

struct A
{
int num;
A()
{
cout<<"A Construct"<<endl;
cout<<"template typename is:"<<typeid(T).name()<<endl;
}
}; //此处的T可省略,X代表模板类型,T和X前的typename可替换成class
template<template<typename T> typename X>
struct B
{
X<double> e;
B()
{
cout << "B Construct" <<endl;
}
}; int main() {
A<B<A>> a;
B<A> b;
return 0;
}

5)inline

扩展用法,可用于定义内联变量,功能与内联函数相似。inline可避免函数或变量多重定义的问题,如果已定义相同的函数或变量(且该函数或变量声明为inline),编译器会自动链接到该函数或变量。如下代码,不发生错误。

//  test.h
inline void print()
{
std::cout << "hello world" << std::endl;
} inline int num = 0;
// func.h
include "test.h"
inline void add(int arg)
{
num += arg;
print();
}
// main.cpp
include "func.h"
int main()
{
num = 0;
print();
add(10);
return 0;
}

2 语法

1)折叠表达式

用于变长参数模板的解包,值支持各种运算符(和操作符),分左、右折叠。如:

template<typename ...T>
auto sum(T ... arg)
{
return (arg + ...); //右折叠
} template<typename ... T>
double sum_strong(T ... arg)
{
return (arg + ... + 0);//右折叠
} template<typename ... T>
double sub1(T ... arg)
{
return (arg - ...);//右折叠
} template<typename ... T>
double sub2(T ... arg)
{
return (... - arg);//左折叠
} int main() {
int s1 = sum(1, 2, 3, 4, 5);//解包:((((1+)2+)3+)4+)5 = 15
double s2 = sum(1.1, 2.2, 3.3, 4.4, 5.5, 6.6);
double s3 = sum(1, 2.2, 3, 4.4, 5); double s4 = sub1(5, 2, 1, 1);//解包:(5-(2-(1-(1)))) = 3
double s5 = sub2(5, 2, 1, 1);//解包:((((5-)2-)1-)1) = 1
double s6 = sum_strong();//s6 = 0
string str1("he");
string str2("ll");
string str3("o ");
string str4("world");
string str5 = sum(str1, str2, str3, str4);//str5 = "hello world"
return 0;
}

2)结构化绑定

用一对包含一个或多个变量的中括号,表示结构化绑定,但是使用结构化绑定是时,须用auto关键字,即绑定时声明变量。

例1:

/*
* 例子:多值返回
*/
struct S
{
double num1;
double num2;
}; S foo(int arg1,double arg2)
{
double result1 = arg1 * arg2;
long result2 = arg2/arg1;
return {result1,result2}; //返回结构体S对象
} int main() {
auto[num1,num2] = foo(10,20.2); //自动推导num1为double,num2为long
cout<<num1<<" "<<num2<<endl;
return 0;
}

例2:

template<typename T, typename U>
struct MyStruct
{
T key;
U value;
}; int main() { list<MyStruct<int,double>> container_list;
container_list.push_back({1,1.1});
container_list.push_back({2,2.2});
container_list.push_back({3,3.3}); map<int,MyStruct<long long,char>> container_map;
container_map[1] = {12,'a'};
container_map[2] = {13,'b'};
container_map[3] = {14,'c'}; for(auto[key,value]:container_list)
{
cout<<"key:"<<key<<",value:"<<value<<endl;
} for(auto[key,value]:container_map)
{
auto [value1,value2] = value;
cout<<"key:"<<key<<",value:{"<<value1<<","<<value2<<"}"<<endl;
} return 0;
}

3)允许非类型模板参数进行常量计算

非类型模板参数可传入类的静态成员。

class MyClass
{
public:
static int a;
}; template<int *arg>
void foo(){}
int main() {
foo<&MyClass::a>();
return 0;
}

4)条件分支语音初始化

在if和switch中可进行初始化。

template<long value>
void foo(int &ok){
if constexpr(ok = 10;value>0)
{}
}
int main() {
int num = 0;
if(int i = 0; i==0)
{ } foo<10>(num); switch(int k = 10;k)
{
case 0:break;
case 1:break;
default:break;
}
return 0;
}

5)聚合初始化

在初始化对象时,可用花括号对其成员进行赋值。

struct MyStruct1
{
int a;
int b;
};
class MyClass
{
public:
int a;
int b;
}; struct MyStruct2
{
int a;
MyStruct1 ms;
};
class MyClassA
{
private:
int a;
int b;
public:
MyClassA(int x,int y):a(x),b(y){}
}; int main() {
MyStruct1 a{10};
MyClass b{10};
MyClassA ma{10,20}; MyStruct2 b1{10, 20};
MyStruct2 c{1, {}};
MyStruct2 d{{}, {}};
MyStruct2 e{{}, {1, 2}}; return 0;
}

6)嵌套命名空间

简化多层命名空间的写法

//传统写法
namespace A
{
namespace B
{
namespace C
{ };
};
}; //新写法
namespace A::B::C
{ };

7)lambda表达式捕获*this的值

lambda表达式可以捕获*this的值,但是this及其成员为只读

struct MyStrcut{
double value = 100.7;
auto f(){
return [this]{
return [*this]{
this->value = 200.2; //error: assignment of member 'MyStrcut::value' in read-only object
return value; //正确
};
}();
}
auto g(){
return []{
return [*this]{}; //错误,外层lambda表达式没有捕获this
}();
}
};

8)枚举[类]对象的构造

可以给枚举[类]对象赋值

enum MyEnum
{
value
};
enum byte : unsigned char { };
struct A { byte b; };
void f(byte){}; int main() {
// MyEnum me {10};//错误:不能用int右值初始化MyEnum类型对象
byte b { 42 }; //正确 // byte c = { 42 }; //错误:不能用int右值初始化byte类型对象
byte d = byte{ 42 }; //正确,其值与b相等
// byte e { -1 }; //错误:常量表达式-1不能缩小范围为byte类型
// A a1 = { { 42 } }; //错误:不能用int右值初始化byte类型对象
A a2 = { byte{ 42 } }; //正确 A a3{ byte{ 42 } }; //正确
// f({ 42 }); //错误:无类型说明符
f(byte{ 42 }); //正确
return 0;
}

9)十六进制单精度浮点数字面值

以0x前缀开头的十六进制数,以f后缀的单精度浮点数合并,就有了十六进制的单精度浮点数。

float value = 0x1111f;

10)基于对齐内存的动态内存分配

新标准中,new和delete运算符增加按照对齐内存值来分配、释放内存空间的功能(即一个新的带内存值的new、delete运算符重载)。

函数原型:

void* operator new(std::size_t size, std::align_val_t alignment);
void* operator new[](std::size_t size, std::align_val_t alignment); void operator delete(void*, std::size_t size, std::align_val_t alignment);
void operator delete[](void*, std::size_t size, std::align_val_t alignment);

参数说明:

size —— 分配的字节数。必须为alignment的整数倍。
alignment —— 指定的对齐内存值。必须是实现支持的合法对齐。
new的返回值:

成功,返回指向新分配内存起始地址的指针。

用例:

struct alignas(8) A{};
int main() {
A *a = static_cast<A *>(::operator new(sizeof(A),static_cast<align_val_t>(alignof(A))));
::operator delete(a, sizeof(A), static_cast<std::align_val_t>(alignof (A)));
return 0;
}

11)细化表达式的计算顺序

为支持泛型编程和重载运算符的广泛使用,新特性将计算顺序进行细化。

如以下有争议代码段:

map<int,int> tmp;
//对于std::map的[]运算符重载函数,在使用[]新增key时,std::map就已经插入了一个新的键值对
tmp[0] = tmp.size(); ////此处不知道插入的是{0, 0}还是{0, 1}

为解决此情况,新的计算顺序规则为:

a)后缀表达式从左到右求值。包括函数调用和成员选择表达式。

b)赋值表达式从右到左求值。包括复合赋值。

c)从左到右计算机位移操作符的操作数。

12)模板类的模板参数自动推导

在c++17之前,类模板构造器的模板参数不能像函数模板的参数一样能被自动推导,比如我们无法写:

std::pair a{1,"a"s}; //c++17
std::pair<int, string> b{1, "b"s}; // C++14及之前
在c++14之前,为了弥补这一缺陷,标准库提供std::make_pair函数,通过函数模板的模板参数实现类型推导。 //相当于std::pair<int, string> c = std::make_pair<int, string>(1, string("c"));
//这里编译器根据 std::make_pair 所带参数的类型,自动推导出了函数模板的参数。
auto c = std::make_pair(1,"c"s);

这个解决方案不太理想,因为:

a)需要记住make_pair,make_tuple这类用于构造模板类的用法。

b)有些make_xxx函数在功能上并不等价于类模板的构造器,比如make_shared等。

在c++17中,这个问题得到了解决,类的模板构造器的模板参数同样能够被自动推导。

std::pair a{1, "a"s}; // C++17
// 相当于
// std::pair<int, string> a{1, "a"s};
// 和函数模板一样,这里编译器根据 std::pair 构造器所带参数类型,自动推导出了构造器模板的参数。

示例:

vector a1 ={1,2,3};//c++17
vector<int> a2 = {1, 2, 3}; // C++14 function f1 = [](int a){return a + 1;}; // C++17
function<int(int)> f2 = [](int a){return a + 1;}; // C++14 tuple t1{1, 2,5, "a"s}; // C++17
tuple<int, double, string> t2{1, 2.5, "a"s}; // C++14
auto t = make_tuple(1, 2,5, "a"s); // C++14 sort(a1.begin(), a1.end(), greater{}); // C++17
sort(a1.begin(), a1.end(), greater<>{}); // C++14
sort(a1.begin(), a1.end(), greater<int>{}); // C++11 map m1 = {std::pair{1, "a"s}, {2, "b"s}}; // C++17
map<int, string> m2 = {{1, "a"s}, {2, "b"s}}; // C++14

自定义类模板中的应用

template<typename T>
struct Container{
Container(T * prt){} //构造器1
Container(T &v){} //构造器2
Container(T const& v) {} // 构造器 3 template<typename D>
Container(T* ptr, D& deleter) {} // 构造器 4
};
struct Deleter {}; int main() {
Container c{(int*)0}; // 调用构造器 1
int x; Container c2{x}; // 调用构造器 2
Container c3{0}; // 调用构造器 3
Deleter d;
Container c4{(int*)0, d}; // 调用构造器 4
//以上编译器自动推导的结果都是 Container<int>
return 0;
}

13)简化重复命名空间的属性列表

[[ using CC: opt(1), debug ]] void f() {}
//作用相同于 [[ CC::opt(1), CC::debug ]] void f() {}

14)不支持、非标准的属性

在添加属性列表时,编译器会忽略不支持的非标准的属性,不会发出警告和错误。

15)改下继承构造函数

在类的继承体系中,构造函数的自动调用时一个令人头疼的问题。c++17引入继承与改写构造的用法。

例一

struct B1
{
B1(int) { std::cout << "B1" << std::endl; }
}; struct D1 : B1 {
using B1::B1;//表示继承B1的构造函数
}; int main() {
D1 d1(0); //正确,委托基类构造函数进行初始化,调用B1::B1(int)
return 0;
}

例二

struct B1
{
B1(int) { std::cout << "B1" << std::endl; }
}; struct B2
{
B2(int) { std::cout << "B2" << std::endl; }
};
struct D1 : B1, B2 {
using B1::B1;//表示继承B1的构造函数
using B2::B2;//表示继承B2的构造函数
};
struct D2 : B1, B2
{
using B1::B1;
using B2::B2;
//正确,D2::D2(int)隐藏了B1::B1(int)和B2::B2(int)。另外由于B1和B2没有默认的构造函数,因此必须显式调用B1和B2的构造函数
D2(int) : B1(1), B2(0)
{ std::cout << "D2" << std::endl; }
}; struct D3 : B1
{
using B1::B1;
};
D3 d3(0);//正确,继承B1的构造函数,即利用B1的构造函数来初始化,输出B1 int main() {
D2 d(100);//编译通过,输出B1 B2 D2
return 0;
}

16)内联变量

参见1.5

17)用auto作为非类型模板参数

当模板参数作为非类型时,可用auto自动推导类型。

template<auto T>
void foo()
{
cout<<T<<endl;
}
int main() {
foo<100>();//输出100
foo<int>(); //error: no matching function for call to 'foo<int>()'
return 0;
}

3 宏

1)__has_include

判断有没有包含某文件。

int main() {

    #if __has_include(<vector>)
std::cout <<"<vector> has included"<<std::endl;
#endif
#if __has_include("istream")
std::cout << "iostream has included" << std::endl;
#endif return 0;
}

4 属性

1)fallthrough

用于switch语句块内,表示会执行下一个case或default。

 int ok1, ok2;
switch (0)
{
case 0:
ok1 = 3;
[[fallthrough]];
case 1:
ok2 = 1;
[[fallthrough]];
}

2)nodiscard

可用于类声明、函数声明、枚举声明中,表示函数的返回值没有被接收,再编译时会出现警告。

[[nodiscard]] class A {}; //该属性在这其实没用
[[nodiscard]] enum class B {}; //该属性在这其实没用
class C {}; [[nodiscard]] int foo()
{ return 10; } [[nodiscard]] A func1() { return A(); }
[[nodiscard]] B func2() { return B(); }
[[nodiscard]] C func3() { return C(); } int main()
{
foo();//warning: ignoring return value
func1();//warning: ignoring return value
func2();//warning: ignoring return value
func3();//warning: ignoring return value
return 0;
}

3)mybe_unused

可用于类、typedef、变量、非静态数据成员、函数、枚举或枚举值中。用于抑制编译器对没用实体的警告。即加上该属性后,对某一实体不会发出“没有用”的警告。

[[maybe_unused]] class A {};
[[maybe_unused]] enum B {};
[[maybe_unused]] int C;
[[maybe_unused]] void fun(){};

c++17 新特性的更多相关文章

  1. Java 17 新特性:switch的模式匹配(Preview)

    还记得Java 16中的instanceof增强吗? 通过下面这个例子再回忆一下: Map<String, Object> data = new HashMap<>(); da ...

  2. C++17 新特性之 std::optional(上)

    最近在学习 c++ 17 的一些新特性,为了加强记忆和理解,把这些内容作为笔记记录下来,有理解不对的地方请指正,欢迎大家留言交流. 引言 在介绍之前,我们从一个问题出发,C++ 的函数如何返回多个值? ...

  3. C++11 & C++14 & C++17新特性

    C++11:C++11包括大量的新特性:包括lambda表达式,类型推导关键字auto.decltype,和模板的大量改进. 新的关键字 auto C++11中引入auto第一种作用是为了自动类型推导 ...

  4. 从Java 9 到 Java 17 新特性梳理

    Java 9 新的创建集合的方法  // [1, 2, 3, 4]  List<Integer> integers = List.of(1, 2, 3, 4);  // {1,2,3}   ...

  5. C++17新特性optional和string_view

    1. optional的作用 类模板 std::optional 管理一个可选的容纳值,即可以存在也可以不存在的值. 一种常见的 optional 使用情况是一个可能失败的函数的返回值.与其他手段,如 ...

  6. Java9至17的新特性总结

    总览 讲讲Java 9-17 的一些语法糖和一些新发布的jeps, 重点讲讲JVM的垃圾回收器 时间线 SpringBoot 为什么选择Java17这个版本.我估计跟下面这个图有关系. Java 8 ...

  7. 你需要了解的 C++ 17 Top 19 新特性(附精彩评论)

    什么是 C++17? C++17(或 C++1z)是继 C++14 之后 C++ 编程语言 ISO/IEC 标准的下一次修订的非正式名称.C++17 现在功能已齐全,正在成为国际标准的路上.它的规范已 ...

  8. JDK 16 正式发布,一次性发布 17 个新特性…不服不行!

    上一篇:Java 15 正式发布, 14 个新特性 JDK 16 正式发布 牛逼啊,JDK 15 刚发布半年(2020/09/15),JDK 16 又如期而至(2021/03/16),老铁们,跟上. ...

  9. 11g新特性-使用DNFS

    NFS相信应该都很熟悉了,但是我们对它的性能一直有所诟病.Oracle在10g版本通过允许对数据库文件直接IO引入ASM.在11g版本中,Oracle对NFS提供了类似的增强,为了改进NFS的性能,开 ...

随机推荐

  1. 【LeetCode】725. Split Linked List in Parts 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  2. 【LeetCode】95. Unique Binary Search Trees II 解题报告(Python)

    [LeetCode]95. Unique Binary Search Trees II 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzh ...

  3. codeforces 624C Graph and String

    C. Graph and String time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  4. Nginx应用场景配置

    Nginx应用全入门 基础回顾 Nginx是什么? Nginx是一个高性能的HTTP和反向代理web服务器,特点是内存少,并发能力强 Nginx能做什么 Http服务器(Web服务器) 反向代理服务器 ...

  5. 理解Android的四种启动模式

    一:前言 四种模式分别为standard, singleTop, singleTask, singleInstance.自己应该明确一个概念先,single到底要single什么.每一个应用app都有 ...

  6. EXPLAINING AND HARNESSING ADVERSARIAL EXAMPLES

    目录 概 主要内容 从线性谈起 非线性 Goodfellow I, Shlens J, Szegedy C, et al. Explaining and Harnessing Adversarial ...

  7. MCMC using Hamiltonian dynamics

    目录 算法 符号说明 Hamilton方程 物理解释 一些性质 可逆 Reversibility H的不变性 保体积 Volume preservation 辛 Symplecticness 离散化H ...

  8. What's new in dubbo-go-pixiu 0.4.0

    Dubbo-go-pixiu 是一款高性能 API 网关,支持 Dubbo 和 Http 等多种协议.具体介绍文章可以参考<Dubbo 跨语言调用神兽:dubbo-go-pixiu>. 近 ...

  9. Jsonschema2pojo从JSON生成Java类(Maven)

    1.说明 jsonschema2pojo工具可以从JSON Schema(或示例JSON文件)生成Java类型, 并且可以配置生成Jackson 1.x,Jackson 2.x, Moshi 1.x或 ...

  10. Docker_部署本地镜像仓库(6)

    在部署本地镜像仓库之前,需要在主机上安装Docker.本地镜像仓库是registry镜像的一个实例,在Docker中运行. 1.创建本地镜像仓库服务 $ docker run -d -p 4000:5 ...