Daily C/C++ 新版本C++中的易用性改进
首先就是auto,自动类型推导
auto可以帮助我们推导表达式的类型,同时这是编译期发生的事,所以和我们自己写类型是一样的,不过可以帮助我们写出更加通用简洁的代码
比如这段代码
template <typename T>
void foo(const T& c)
{
using std::begin;
using std::end;
for (auto it = begin(c),
ite = end(c);
it != ite; ++it) {
// 循环体
}
}
使用了C++11提供的全局begin和end,可以帮助我们获得容器的范围,这个对于C的数组也同样适用。而且这个可以帮助我们更好的进行抽象,因为普通的迭代器并不能直接替代指针,所以如果不用auto的话,我们需要单独重载C数组版本的,用指针,还需要重载C++容器,用迭代器
对于auto的推导规则, 实际上和模板的推导规则类似,我们可以理解成auto只推到纯的类型,而对于const,&等修饰需要我们自己来加
auto a = expr //值类型
auto &a = expr //引用类型
auto &&a = expr //万能引用,得到的是expr类型
然后是decltype,用于获得表达式的类型
对于变量名,他会获得精确的类型,对于(变量名)
等表达式的情况,会获得引用,而对于纯右值,则会获得值类型。可以这么想,因为纯右值就是个字面量,而将亡值好歹也是个对象。
还是举例子,比如我们有int a
decltype(a) // int
decltype((a)) // int&
decltype(a + a) // int
那么其实我们是用decltype和auto来配合的,因为auto需要我们指定那些修饰符,所以我们出现了这样的用法decltype(auto) a = expr
这样可以创造和expr相同类型的值,而无需去管expr的类型具体是什么
decltype(auto)
同样可以用作返回值类型推断,让返回表达式决定返回值的类型
当然还有对应的后置类型声明
auto foo(arguments) -> type {
// body
}
在C++17之后,我们可以自动推导模板类型
//before
pair<int, int> pr{1, 2}
auto pr = make_pair(1, 2)
//after
pair pr{1, 2}
同时这种自动推导的机制,是可以通过编译器自动生成,也可以手动指定
比如这里还是引用吴老师的代码
template <typename T>
struct MyObj {
MyObj(T value);
…
};
MyObj(const char*) -> MyObj<string>;
MyObj obj{"hello"};
// 得到 MyObj<string>
可以看到我们可以指定char*类型推导为string类型的对象
然后就是结构化绑定,相信大家平时做leetcode已经用了不少了
就是最常见的 auto [key, val] : mp
这样可以把哈系表中的键值一一输出出来,而不需要用tie来进行匹配
否则你就要写 std::tie(key, val) = *it // it为mp的迭代器
接着是初始化相关的,初始化列表可以让我们用可变长度大小的list来初始化容器
统一初始化则是让我们用大括号来进行对象的初始化,以代替小括号
使用这个方法只有一个小问题,就是如果构造函数有初始化列表的重载版本,那么他会千方百计的调用初始化列表的那个版本
所以可以这样用,如果类没有初始化列表,那么统一用大括号初始化, 否则的话就只在用初始化列表的时候用大括号
最后是类成员的默认初始化,我们可以在声明数据成员的时候在他的后面加上一个初始化的表达式,那么当使用的构造函数中的初始化列表中不含有这个数据成员时,我们就使用这个成员自己的初始化表达式
借用老师的代码
class Complex {
public:
Complex() {}
Complex(float re) : re_(re) {}
Complex(float re, float im)
: re_(re) , im_(im) {}
private:
float re_{0};
float im_{0};
};
用第一个构造函数就会使得第二个数据成员使用他自己的初始化表达式
这样可以有效帮助我们简化构造函数的代码,同时也防止出现成员初始化失败的情况。
文章评论