C++14~17新特性

C++ 14、17

C++14相对于11并没有太大的改动,14只是对11来说一个比较小的改动,但是在很大程度上完善了11。

一 、C++14 新特性

变量模板

在C++11及之前,模板只针对函数与类。14之后可以使用变量模板

template<class T>
constexpr T Pi = T(3.14159);


template<class T>
T GetCircleArea(T radius)
{
	return Pi<T> *radius * radius;
}
模板别名
template<class T,class U>
struct object
{
	T _val;
	U _val2;
};

template<class T,class U>
using child = object<T, U>;
Lambda表达式新增的功能
// 1.支持在Lambda表达式中使用auto定义变量类型
auto Lambda = [](auto& param){};

// 2.支持对捕获的变量和引用进行初始化
int x = 10;
auto functor = [&r = x, y = x + 1]() {
	return r * y;
};

字面量

二进制字面量:支持使用0b开头的数字作为表示二进制

int bin = 0b10101001110; // 1358

数字分隔符:支持在数字中使用单引号进行分割

int a = 123'456'789; // 123456789
std::exchange

std::exchange用于移动语义,可以指定新值替换旧值,并返回旧值。其源码如下:

template<class T, class U = T>
constexpr // since C++20
T exchange(T& obj, U&& new_value)
{
    T old_value = std::move(obj);
    obj = std::forward<U>(new_value);
    return old_value;
}
constexpr增强

C++程序一般要经历编译,链接,运行三个阶段,非常量表达式只能在程序运行阶段计算出结果,而常量表达式的计算往往在编译阶段,这可以极大的提升程序的运行效率。 constexpr能让用户显式声明的函数或构造函数在编译期会成为常量表达式 ,C++11中constexpr只能包含一个返回语句。14中放宽了要求,允许在constexpr修饰的函数中声明变量、使用循环和条件语句等:

constexpr bool isPrimitive(int number)
{
	if (number <= 0)
	{
		return false;
	}
	for (int i = 2; i <= sqrt(number) + 1; ++i)
	{
		if (number % i == 0)
		{
			return false;
		}
	}
	return true;
}
std::quoted

C++14引入std::quoted用于给字符串添加双引号

string str = "hello world";
cout << str << endl;			// hello world
cout << std::quoted(str) << endl;  //"hello world"

二、C++17新特性

if/switch 中定义变量

C++17之前if/switch语句中不能声明一个临时的变量

std::vector<int> vec = {1, 2, 3, 4};

//C++17
// 将临时变量放到 if 语句内
if (const std::vector<int>::iterator itr = std::find(vec.begin(), vec.end(), 3); itr != vec.end()) {
    *itr = 4;
}
结构化绑定
  • 值拷贝方式绑定
auto [key, value] = std::make_pair<int, std::string>(1, "名字");
  • 左值引用方式绑定
auto& [key, value] = std::make_pair<int, std::string>(1, "名字");
  • 右值引用方式绑定,支持移动语义
auto&& [key, value] = std::make_pair<int, std::string>(1, "名字");

可以用来绑定std::pairstd::tuplestd::array数组和结构体。

std::tuple<int, double, std::string> f() {
    return std::make_tuple(1, 2.3, "456");
}

// 结构化绑定tuple
auto [x, y, z] = f();

// 绑定数组
int array[3] = {1,2,3};
auto [a,b,c] = array;

// 绑定结构体,成员属性需要是public
struct FObject {
public:
	int x;
	int y;
};

auto [a, b] = FObject{ 1,2 };
if constexpr控制流

constexpr是将表达式或函数编译为常量结果,如果将这一特性引入条件判断中去,让代码在编译时就完成分支的判断,可以提高程序的效率。

template<typename T>
auto print_type_info(const T& t) {
	if constexpr (std::is_integral<T>::value) 
	{
		return t + 1;
	} else {
		return t + 0.001;
	}
}
namespace嵌套
// C++17之前
namespace orgin{
	namespace animal{
		namespace bird{
		
		}
	}
}

// C++17
namespace orgin::animal::bird{}
新增Attribute属性

在C++11和14中的Attribute

[[noreturn]] 函数不会返回
[[deprecated]] 函数将弃用的警告

C++17新增[[nodiscard]] 用于强调函数的返回值不会被丢弃,否则会出现编译器警告。

[[nodiscard]] int foo();
void bar() {
    foo(); // Warning emitted, return value of a nodiscard function is discarded
}

[[fallthrough]],用在switch中提示可以直接落下去,不需要break,让编译期忽略警告。

switch (i) {}
    case 1:
        xxx; // warning
    case 2:
        xxx; 
        [[fallthrough]];      // 警告消除
    case 3:
        xxx;
       break;
}
constexpr lambda表达式

C++17前lambda表达式只能在运行时使用,C++17引入了constexpr lambda表达式,可以用于在编译期进行计算。

 constexpr auto lamb = [] (int n) { return n * n; };

在C++17,constexpr修饰的函数,限制如下:

函数体不能包含汇编语句、goto语句、labeltry块、静态变量、线程局部存储、没有初始化的普通变量,不能动态分配内存,不能有new delete等,不能虚函数。

lambda中的*this对象副本捕捉

C++17之前lambda表达式中访问类的对象成员变量只能捕获this,但是这里捕获的是this指针正常情况下可能没问题,但是如果多线程情况下,函数的作用域超过了对象的作用域,对象已经被析构的情况下,在访问该对象就会有很多问题。C++17新增了可以捕获*this,持有的是对象的副本。

struct FObject {
    int a;
    void ObjectFunc() {
        auto lambda = [*this](){
            cout << a << endl;
        };
    }  
};
std::optional更加优雅的写出无返回结果的函数

使用std::optional<T>来修饰函数返回值,表明这个函数可能不会返回值,T代表原有的返回类型。

std::map<int, std::string> user_map = { {1,"unreal"} };

std::optional<std::string> FindName(int Id)
{
	if (user_map.find(Id) != user_map.end())
	{
		return user_map[Id];
	}
	return std::nullopt;
}
// 使用
std::optional<std::string> result = FindName(1);
if (result.has_value())
{
	std::string value = *result;
}
内联变量

使用内联变量可以用来初始化静态成员变量。

struct FObject{
	inline static const int val = 10;
};
std::variant

C++17添加了std::variant用来对union联合体的升级,许多union不支持的类型,比如std::map,std::string都可以在variant里得到支持。

std::variant<int,double,std::string> varIns;
varIns = "123";
std::cout<< std::get<std::string>(varIns)<<std::endl;
varIns = 10.0;
std::cout<< std::get<double>(varIns)<<std::endl;

std::get会抛出异常,使用std::get_if进行替代

if(auto*str = std::get_if<std::string>(varIns))
{
	std::cout<<*str<<std::endl;
}
std::any

C++17引入std::any可以存储任意类型的变量。

struct FObject {
	int value = 0b1111;
};

// 使用
std::string str = "unreal";

std::any a1 = str;
std::any a2 = 10;
std::any a3 = FObject();

std::cout << std::any_cast<std::string>(a1) << std::endl;
std::cout << std::any_cast<int>(a2) << std::endl;
std::cout << std::any_cast<FObject>(a3).value << std::endl;

如果转换不成功的话,会抛出bad_any_cast异常

try
{
	std::cout << std::any_cast<int>(a1) << std::endl;
}
	catch (std::bad_any_cast& e)
{
	std::cout << e.what() << std::endl;
}

可以通过传入参数的指针,转换失败就不会抛出异常

if (auto* ptr = std::any_cast<int>(&a1))
{}
std::apply

std::apply可以将tuple作为函数参数传入

void functest(int val, float val2, double val3)
{};

// 调用
std::apply(functest,std::make_tuple(1,2.f,5.234231));
std::string_view

C++17引入std::string_view协助程序员更高效的使用只读字符串。当遇到需要使用只读字符串,尤其是传入只读字符串作为函数参数时,优先使用std::string_view。string_view作为只用于显示,**不拥有所有权,并且不可变!**通常内部实现里只有两个数据成员,一个指向数据的指针,二个是数据的size,类型一般为size_t。在64位系统中一般大小为16个字节,比std::string小!

以前的写法

void printstr(const std::string& str)
{}

// 方法1
std::string str("unreal");
printstr(str);

// 方法2
const char* str2 = "unity";
printstr(str2);

// 方法3
printstr("cocos"); 

以上方法方法3的性能开销最大,1和2的开销差不多。方法3传入的是一个纯右值字符串字面量,会创建出一个临时std::string对象,而const T& 类型可以接受一个纯右值引用,将该变量的生命周期延续到函数结束。

但使用const std::string&容易造成一些bug,比如悬垂引用:

const std::string& printstr(const std::string& str)
{
    return str;
}

auto& str = printstr("unreal");

在函数调用中绑定到函数形参的临时变量,生命周期只持续到函数结尾。如果函数返回一个生命周期长于表达式的引用,那它将会成为一个悬垂引用!

现在的写法。现在使用std::string_view替代const std::string&作为字符串的显示。

void printstr(std::string_view str)
{
	std::cout<<str<<std::endl;
}

std::string str("unreal");
printstr(str);
const char* str2 = "unity";
printstr(str2);
printstr("cocos"); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值