码迷,mamicode.com
首页 > 编程语言 > 详细

c++11 新特性

时间:2018-03-22 11:00:05      阅读:153      评论:0      收藏:0      [点我收藏+]

标签:哈希   操作符   inter   编译参数   cto   实现   需要   post   推导   

1.auto类型推导

在早期版本中,关键字auto主要是用于声明具有自动存储期的局部变量。也就是说,是局部变量,除了static类型以外,其他变量(以“数据类型+变量名”的方式定义)都默认为具有自动存储期,所以auto关键字可有可无。

在C++11的版本中,删除了auto原本的功能,并进行了重新定义了。即C++11中的auto具有类型推导的功能。

静态类型就是在使用变量前需要先定义变量的数据类型

动态类型无需定义变量的数据类型

静态类型是在编译时进行类型检查,而动态类型是在运行时进行类型检查。

auto就相当于一个类型声明时的占位符,而不是一种“类型”的声明,在编译时期编译器会将auto替代成变量的实际类型。

auto的优势

对类型复类型变量声明时的简化代码.也避免一些在类型声明时的错误.最常见的就是迭代器的类型

std::vector<std::string> array;
std::vector<std::string>::iterator it = array.begin();

auto it = array.begin();

 

当在需要double类型的时候避免定义为整数类型,使用auto,会自动推导.

 

不能将auto用于声明函数形参,貌似c++14中支持了.

 

auto限制

auto可以自动推断出指针,但是无法自动推断出引用

// 等价定义
auto* a = &x;
auto a = &x

// 费等价,第一个是值语义,第二个是引用
auto a=x;
auto &a=x;

 

auto 会自动删除const(常量性),volatile(易失性)。

对于引用和指针,即auto*, auto&仍会保持const与volatile。

 

当定义多个变量在一行,所有变量的类型都要一致,且为第一个变量的类型,否则编译出错。

 

定义模板函数时,用于声明依赖模板参数的变量类型。就是对形参的运算,其结果在编译出来之前是未知的,可以使用auto来推到.类似的,依赖于模板参数的返回值也同样.

 

auto 变量必须在定义时初始化,类似于引用.

如果初始化表达式是引用,则去除引用语义。

如果初始化表达式为const或volatile(或者两者兼有),则除去const/volatile语义。

如果auto关键字带上&号,则不去除const语意。

初始化表达式为数组时,auto关键字推导类型为指针。

函数或者模板参数不能被声明为auto

auto仅仅是一个占位符,它并不是一个真正的类型,不能使用一些以类型为操作数的操作符,如sizeof或者typeid。

 

2 decltype 类型推导

 decltype() 在有一个对象或是类型的时候,根据这个对象或是类型去推导出该类型,并用其创建对象.或是在啊该类型后继续加引用,或是指针,因此,需要对应的赋值需要与推导出的类型相符合

 

因此,其可以作为函数参数,因为他需要的对象,已经有类型了,而不是凭空产生.

类型推导是随着模板和泛型编程的广泛使用而引入的。在非泛型编程中,类型是明确的,而在模板与泛型编程中,类型是不明确的,它取决于传入的参数类型。

decltype与我前面讲到的auto还是有一些共同点的,如二者都是通过推导获得的类型来定义另外一个变量,再如二者都是在编译时进行类型推导。不过他们类型推导的方式有所不同,auto是通过初始化表达式推导出类型,而decltype是通过普通表达式的返回值推导出类型。

 typeid 与 decltype

运行时类型识别(RTTI),

 RTTI机制:为每个类型产生一个type_info类型数据,程序员可以在程序中使用typeid随时查询一个变量的类型,typeid就会返回变量相应的type_info数据,type_info的name成员可以返回类型的名字。在C++11中,增加了hash_code这个成员函数,返回该类型唯一的哈希值,以供程序员对变量类型随时进行比较。

直接对type_info.name进行字符串比较不就可以了么,为什么还要给每个类型一个哈希值?我认为,字符串比较的开销也是比较大的,如果用每个类型来对于一个哈希值,通过比较哈希值确定类型是否相同的方法,会比使用字符串比较的效率要高得多。

typeid(b).name()
typeid(a).hash_code() == typeid(b).hash_code()

 RTTI在运行时才确定出类型,而更多的需求是在编译时确定类型。并且,通常的程序是要使用推导出来的这种类型而不是单纯地识别它。

using size_t = decltype(sizeof(0));

 decltype推导的四规则如下:

  1. 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,可能会导致编译错误;
  2. 假设e的类型是T,如果e是一个将亡值(xvalue), 那么decltype(e)为T&&
  3. 假设e的类型是T,如果e是一个左值,则decltype(e)为T&;
  4. 假设e的类型是个T, 则decltype(e)为T.

3,4的区别在于,3中传入的是一个运算后的结果.而4传入的是单个变量,没有运算

例如

int i=1;
decltype((i)) a=0; // () 运算了,是 int&
decltype(++i) b=1; // ++i是运算后的结果.推断为 int &
decltype(i++) c =1; // 先推断 i ,再自增,因此是 int
decltype(arr[1]) d=1; // 运算后的结果,因此是引用.

 对于第一个,对于数组类型,decltype可以推导出 int [10] 完整的类型.

 

decltype能够“带走”表达式的cv限制符。不过,如果对象的实例的定义中有cv限制符时,其实例的成员不会继承const或volatile限制符。

# include <iostream>
# include <type_traits>
using namespace std;

const int ic = 0;
volatile int iv;
struct S {
    int i;
};
const S a = {0};
volatile S b;
volatile S* p = &b;

int main(void) {
    cout << is_const<decltype(ic)>::value << endl; // 1
    cout << is_volatile<decltype(iv)>::value << endl; // 1
    cout << is_const<decltype(a)>::value << endl; // 1
    cout << is_volatile<decltype(b)>::value << endl; // 1
    cout << is_const<decltype(a.i)>::value << endl; // 0
    cout << is_volatile<decltype(p->i)>::value << endl; // 0
    return 0;
}

 

模板中,但返回值与参数有关的时候,比如是参数解引用后的类型,C++11引进了新语法——追踪返回类型,来声明和定义这样的函数

template<typename T1, typename T2>
auto Sum(T1& t1, T2& t2)->decltype(t1+t2) {
    return t1+t2;
}

 

3 范围for

 

基于范围的for循环,结合auto的关键字,程序员只需要知道“我在迭代地访问每一个元素”即可,而再也不必关心范围,如何迭代访问等细节。

如果范围for中使用auto推断类型,那么auto不会自动推导出引用类型,如需引用要加上&

 

4 nullptr

而典型的初始化指针通常有两种:0与NULL, 意在表明指针指向一个空的位置。

NULL其实是宏定义

// stddef.h
# undef NULL
# if define(_cplusplus)
# define NULL 0
# else
# define NULL ((void*)0)
# endif

 NULL既可被替换成整型0,也可以被替换成指针(void*)0。这样就可能会引发一些问题,如二义性:

在重载函数的时候,如果重载函数有接受int类型和接受void*类型的,那么传入NULL就会报错.

 

nullptr_t为nullptr的类型, 称为指针空值类型。

所有定义为nullptr_t类型的数据都是等价的,行为也是完全一致的

nullptr_t类型的数据可以隐式转换成任意一个指针类型。

nullptr_t类型数据不能转换成非指针类型,即使用reinterpret_cast()的方式也不可以实现转化;可以强转,但是报错提示精度损失,加上编译参数以后,强转会警告,值为0

nullptr_t类型的对象不适用于算术运算的表达式;

nullptr_t类型数据可以用于关系运算表达式,但仅能与nullptr_t类型数据或者是指针类型数据进行比较,当且仅当关系运算符为-=, <=, >=, 等时返回true。

 

null与(void*)0

nullptr是编译时期的常量,它的名字是一个编译时期的关键字,能够为编译器所识别,而(void*)0只是一个强制类型转化的表达式,其返回值也是一个void*的指针类型。
nullptr 能够隐式转化成指针,而(void*)0只能进行显示转化才能变成指针类型(c++11)。虽然在c++标准中(void*)类型的指针可以实现隐式转化。

6 __cplusplus

用与编译器判断使用的是什么编译器.

当使用c++的编译器,例如g++ clang++那么会自动定义这个宏.但是编译出来的程序又需要被c调用,或是调用c.则需要编译为c

所以使用该宏

代码开头:
#if defined(__cplusplus) || defined(c_plusplus)
extern "C"
{
#endif

 

代码结尾:
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif

 

7 对齐方式

c++11支持按照任意的方式进行对齐

alignas(8) char c[1024];

 其中括号内也可以是一个类型,根据类型再推断出该类型的字节大小.

alignof可以返回对齐的大小

int  i = alignof(a);

 

8 显示的默认或是删除的构造系列函数

9 委托构造函数

 

10 显示转换操作符

11 初始化列表

12 lambda函数

 

c++11 新特性

标签:哈希   操作符   inter   编译参数   cto   实现   需要   post   推导   

原文地址:https://www.cnblogs.com/perfy576/p/8620784.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!