首页 > 分享 > C++17新特性个人总结

C++17新特性个人总结

目录

C++17

1 关键字

1.1 constexpr

1.2 static_assert

1.3 auto

1.4 typename

1.5 inline

2 语法

2.1 折叠表达式

2.2 结构化绑定

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

2.4 条件分支语句初始化

2.5 聚合初始化

2.6 嵌套命名空间

2.7 lambda表达式捕获*this的值

2.8 枚举[类]对象的构造

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

2.10 基于对齐内存的动态内存分配

2.11 细化表达式的计算顺序

2.12 模板类的模板参数自动推导

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

2.14 不支持、非标准的属性

2.15 改写与继承构造函数

2.16 内联变量

2.17 用auto作为非类型模板参数

3 宏

3.1 __has_include

4 属性

4.1 fallthrough

4.2 nodiscard

4.3 maybe_unuse

结语

C++17

编译器版本:GCC 7.1、Clang 5.0

__cplusplus:201703L

编译选项:-std=c++17

1 关键字

1.1 constexpr

扩展constexpr使用范围,可用于if语句中,也可用于lambda表达式中。

例子1:

#include<iostream>

template<bool ok>

constexpr void foo()

{

if constexpr (ok == true)

{

std::cout << "ok" << std::endl;

}

else

{

std::cout << "not ok" << std::endl;

}

}

int main()

{

foo<true>();

foo<false>();

return 0;

}

例子2:

int main()

{

constexpr auto add1 = [](int n, int m){

auto func1 = [=] { return n; };

auto func2 = [=] { return m; };

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)( );

int sum2 = add2(sum1, 4);

constexpr int sum3 = add3(1, 2);

int sum4 = add2(10, 2);

return 0;

}

1.2 static_assert

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

如:

static_assert(true, "");

static_assert(true);

1.3 auto

扩展auto的推断范围

如:

auto x1 = { 1, 2 };

auto x2 = { 1, 2.0 };

auto x3{ 1, 2 };

auto x4 = { 3 };

auto x5{ 3 };

1.4 typename

扩展用法,允许出现在模板的模板的参数中。

首先回顾一下typename的用法,①用于模板中,表示模板参数为类型;②用于声明某名字是变量名

如例1:

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;

}

'

新特性下的typename用法,

如例2:

#include<iostream>

#include<typeinfo>

template<typename T>

struct A

{

int num;

A()

{

std::cout << "A Construct" << std::endl;

std::cout << "template typename is: " << typeid (T).name() << std::endl;

}

};

template<template<typename T> typename X>

struct B

{

X<double> e;

B() { std::cout << "B Construct" << std::endl; }

};

int main()

{

A<B<A>> a;

std::cout << "***************************" << std::endl;

B<A> b;

return 0;

}

运行结果:

1.5 inline

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

如(不发生错误):

inline void print()

{

std::cout << "hello world" << std::endl;

}

inline int num = 0;

include "test.h"

inline void add(int arg)

{

num += arg;

print();

}

include "func.h"

int main()

{

num = 0;

print();

add(10);

return 0;

}

2 语法

2.1 折叠表达式

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

如:

#include<string>

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, 2, 4, 5);

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);

double s5 = sub2(5, 2, 1, 1);

double s6 = sum_strong();

std::string str1("he");

std::string str2("ll");

std::string str3("o ");

std::string str4("world");

std::string str5 = sum(str1, str2, str3, str4);

return 0;

}

2.2 结构化绑定

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

例子1:

struct S

{

double num1;

long num2;

};

S foo(int arg1, double arg2)

{

double result1 = arg1 * arg2;

long result2 = arg2 / arg1;

return {result1, result2};

};

int main()

{

auto [num1, num2] = foo(10, 20.2);

return 0;

}

'

例子2:

#include<list>

#include<map>

template<typename T, typename U>

struct MyStruct

{

T key;

U value;

};

int main()

{

std::list<MyStruct<int, double>> Container1;

std::map<int, MyStruct<long long, char>> Container2;

for(auto [key, value] : Container1)

{

}

for(auto [key, value] : Container2)

{

auto [value1, value2] = value;

}

return 0;

}

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

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

如:

class MyClass

{

public:

static int a;

};

template<int *arg>

void foo() {}

int main()

{

foo<&MyClass::a>();

return 0;

}

'

2.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;

}

'

2.5 聚合初始化

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

如:

struct MyStruct1

{

int a;

int b;

};

struct MyStruct2

{

int a;

MyStruct1 ms;

};

int main()

{

MyStruct1 a{10};

MyStruct2 b{10, 20};

MyStruct2 c{1, {}};

MyStruct2 d{{}, {}};

MyStruct2 e{{}, {1, 2}};

return 0;

}

'

2.6 嵌套命名空间

简化多层命名空间的写法

如:

namespace A

{

namespace B

{

namespace C

{

};

};

};

namespace A::B::C

{

};

2.7 lambda表达式捕获*this的值

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

如:

struct MyStruct {

double ohseven = 100.7;

auto f() {

return [this] {

return [*this] {

this->ohseven = 200.2;

return ohseven;

};

}();

}

auto g() {

return []{

return [*this]{};

}();

}

};

2.8 枚举[类]对象的构造

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

如:

enum MyEnum { value };

MyEnum me {10};

enum byte : unsigned char { };

byte b { 42 };

byte c = { 42 };

byte d = byte{ 42 };

byte e { -1 };

struct A { byte b; };

A a1 = { { 42 } };

A a2 = { byte{ 42 } };

void f(byte);

f({ 42 });

enum class Handle : unsigned int { value = 0 };

Handle h { 42 };

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

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

如:

int main()

{

float value = 0x1111f;

return 0;

}

'

2.10 基于对齐内存的动态内存分配

谈到动态内存分配,少不了new和delete运算符,新标准中的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的返回值:

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

用法例子:

#include<new>

struct alignas(8) A {};

int main()

{

A *a = static_cast<A *>(::operator new(sizeof(A), static_cast<std::align_val_t>(alignof (A))));

::operator delete(a, sizeof(A), static_cast<std::align_val_t>(alignof (A)));

return 0;

}

2.11 细化表达式的计算顺序

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

如以下争议代码段:

#include<map>

int main()

{

std::map<int, int> tmp;

tmp[0] = tmp.size();

return 0;

}

为了解决该情况,新计算顺序规则为:

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

②赋值表达式从右向左求值。这包括复合赋值。

③从左到右计算移位操作符的操作数。

2.12 模板类的模板参数自动推导

定义模板类的对象时,可以不指定模板参数,但必须要在构造函数中能推导出模板参数

如:

template<class T> struct A {

explicit A(const T&, ...) noexcept {}

A(T&&, ...){}

};

int i;

A a1 = { i, i };

A a2{i, i};

A a3{0, i};

A a4 = {0, i};

template<class T> A(const T&, const T&) -> A<T&>;

template<class T> explicit A(T&&, T&&) -> A<T>;

A a5 = {0, 1};

A a6{0, 1};

A a7 = {0, i};

A a8{0, i};

template<class T>

struct B {

template<class U>

using TA = T;

template<class U>

B(U, TA<U>);

};

B b{(int*)0, (char*)0};

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

如:

[[ using CC: opt(1), debug ]] void f() {}

2.14 不支持、非标准的属性

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

2.15 改写与继承构造函数

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

例子1:

#include<iostream>

struct B1

{

B1(int) { std::cout << "B1" << std::endl; }

};

struct D1 : B1 {

using B1::B1;

};

D1 d1(0);

例子2:

#include<iostream>

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;

using B2::B2;

};

D1 d1(0);

struct D2 : B1, B2

{

using B1::B1;

using B2::B2;

D2(int) : B1(1), B2(0)

{ std::cout << "D2" << std::endl; }

};

struct D3 : B1

{

using B1::B1;

};

D3 d3(0);

int main()

{

D2 d(100);

return 0;

}

例子3:

#include<iostream>

struct B1

{

B1() { std::cout << "B1 default" << std::endl; }

B1(int) { std::cout << "B1" << std::endl; }

};

struct B2

{

B2() { std::cout << "B2 default" << std::endl; }

B2(int) { std::cout << "B2" << std::endl; }

};

struct D1 : B1, B2

{

using B1::B1;

using B2::B2;

D1(int) : B1(1), B2(0)

{ std::cout << "D2" << std::endl; }

D1() { std::cout << "D2 default" << std::endl; }

};

int main()

{

D1 d(100);

D1 dd;

return 0;

}

2.16 内联变量

见1.5

2.17 用auto作为非类型模板参数

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

如:

#include<iostream>

template<auto T>

void foo()

{

std::cout << T << std::endl;

}

int main()

{

foo<100>();

foo<int>();

return 0;

}

3 宏

3.1 __has_include

判断有没有包含某文件

如:

int main()

{

#if __has_include(<cstdio>)

printf("hehe");

#endif

#if __has_include("iostream")

std::cout << "hehe" << std::endl;

#endif

return 0;

}

4 属性

4.1 fallthrough

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

如:

int main()

{

int ok1, ok2;

switch (0)

{

case 0:

ok1 = 0;

[[fallthrough]];

case 1:

ok2 = 1;

[[fallthrough]];

}

return 0;

}

'

4.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();

func1();

func2();

func3();

return 0;

}

'

4.3 maybe_unused

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

用法例子:

[[maybe_unused]] class A {};

[[maybe_unused]] enum B {};

[[maybe_unused]] int C;

[[maybe_unused]] void fun();

结语

本次检验C++17新特性使用了GCC编译器,对于Clang的支持性方面没有做出差异测试。若有问题,欢迎指出

相关知识

前台试用期个人总结
儿科护士年度考核个人总结
园艺个人总结范文
园林个人年度总结11篇
学习园林技术个人总结(通用19篇)
林业站年度个人总结11篇
环保活动个人总结
绿化工作个人总结(通用23篇)
绿化工作个人总结(通用6篇)
绿化养护工作总结个人总结8篇

网址: C++17新特性个人总结 https://m.huajiangbk.com/newsview1059347.html

所属分类:花卉
上一篇: 秋季葡萄轻松排毒抗衰老
下一篇: 紫荆花的寓意