当前位置:   article > 正文

c++11/14/17新特性_c++ 11 14 17主要特性

c++ 11 14 17主要特性

1. C++语言历程

C++语言从1983年正式诞生以来,经历了多次的修订与改版,主要从包含两个大的节点,一是1998年,C++语言正式被C++标准委员会纳入标准,二是2011年,C++语言新增了许多新的特性,大大提升C++语言的实用性。可以把C++标准分成两个大的版本,C++1.0(C++98,C++03,C++03(tr1))和C++2.0(C++11,C++14,C++17,C++20(草案))
在这里插入图片描述
C++1.0和2.0的主要特性

C++版本特性
C++98主要集成在兼容C特性的基础上增加面向对象的特性,包括类、简单继承、内联机制、函数默认参数以及强类型检查,虚函数、函数重载、引用机制(符号为&)、const关键字以及双斜线的单行注释,多重继承、保护成员以及静态成员等特性,并集成了标准模板库STL
C++03主要修订C++98中存在的问题
C++03(tr1)发布C++希望引入新特性的报告,但没有完全引入,后期加入部分std::tr1相关的功能
C++11新增正则表达式(正则表达式详情)、完备的随机数生成函数库、新的时间相关函数,原子操作支持、标准线程库(2011之前,C和C++语言均缺少对线程的支持)、一种能够和某些语言中foreach语句达到相同效果的新的for语法、auto关键字、新的容器类、更好的union支持、数组初始化列表的支持以及变参模板的支持等等新特性
C++14主要对C++11变准文案描述进行了修正
C++17简化C++语言的日常使用
C++20

2. 各编译器对C++标准核心功能的支持

C++11核心功能MSVCGCCICCClang替代方案
Rvalue references10.04.312.02.9Boost.Move
Rvalue references for *thisNov 134.8.114.02.9
Initialization of class objects by rvalues9.04.311.12.9
Non-static data member initializers12.04.714.03.0
Variadic templatesNov 124.312.12.9
Extending variadic template template parametersNov 124.412.12.9
Initializer listsNov 124.414.03.1
Static assertions10.04.311.12.9Boost.StaticAssert
auto-typed variables10.04.412.02.9Boost.Typeof
Multi-declarator auto10.04.412.02.9Boost.Typeof
Removal of auto as a storage-class specifier10.04.412.02.9Boost.Typeof
New function declarator syntax10.04.412.02.9Boost.ReturnType
New wording for C++11 lambdas10.04.512.03.1Boost.Lambda
Declared type of an expression10.04.312.02.9Boost.Typeof
Incomplete return types11.04.8.112.13.1
Right angle brackets9.04.311.12.9TR1
Default template arguments for function templatesNov 124.312.12.9
Solving the SFINAE problem for expressionsNo4.412.12.9
Template aliases12.04.712.13.0
Extern templates9.04.311.12.9
Null pointer constant10.04.612.13.0自己实现的null_ptr
Strongly-typed enums11.04.414.02.9#define
Forward declarations for enums11.04.614.03.1
Generalized attributes14.04.812.13.3
Generalized constant expressionsNov 134.614.03.1
Alignment supportNov 134.8No3.3
Delegating constructorsNov 124.714.03.0
Inheriting constructorsNov 134.815.03.3
Explicit conversion operatorsNov 124.514.03.0
New character types14.04.414.02.9
Unicode string literals14.04.514.03.0
Raw string literalsNov 124.514.03.0
Universal character name literals14.04.512.13.1
User-defined literalsVS14 CTP14.715.03.1
Standard Layout Types11.04.5No3.0
Defaulted and deleted functions12.04.412.03.0
Extended friend declarations10.04.712.02.9
Extending sizeofNov 134.414.03.1
Inline namespaces14.04.414.02.9
Unrestricted unions14.04.614.03.1
Local and unnamed types as template arguments9.04.512.02.9
Range-based for11.04.613.03.0Boost.Foreach
Explicit virtual overrides11.04.714.03.0#define
Minimal support for garbage collection and reachability-based leak detection10.0NoNoNo
Allowing move constructors to throw [noexcept]Nov 134.614.03.0
Defining move special member functions14.04.614.03.0
Sequence points12.04.815.03.3
Atomic operations11.04.413.03.1Boost.Atomic
Strong Compare and Exchange11.04.813.03.1Boost.Atomic
Bidirectional Fences11.04.813.03.1Boost.Atomic
Memory model12.04.8No3.2Boost.Atomic
Data-dependency ordering: atomics and memory model11.04.8No3.2Boost.Atomic
Propagating exceptions10.04.412.02.9Boost.Exception
Abandoning a process and at_quick_exit14.04.8NoNo
Allow atomics use in signal handlers12.04.8No3.1
Thread-local storage14.04.8No3.3Boost.Thread
Dynamic initialization and destruction with concurrencyNov 134.8No2.9
func predefined identifierNov 134.311.12.9FUNCTION
C99 preprocessorNo4.311.12.9
long long9.04.311.12.9__int64
Extended integral typesNoNoNoNo
C++14核心功能MSVCGCCICCClang替代方案
Tweak to certain C++ contextual conversions12.04.916.03.4
Binary literals14.04.911.02.9
Return type deduction for normal functionsNov 134.915.03.3
Generalized lambda capture (init-capture)14.04.915.03.4
Generic (polymorphic) lambda expressionsNov 134.916.03.4
Variable templates15.05.0No3.4
Relaxing requirements on constexpr functions15.05.0No3.4
Member initializers and aggregates15.05.016.03.3
Clarifying memory allocationNoNoNo3.4
Sized deallocation14.05.017.03.4
[[deprecated]] attribute14.04.916.03.4
Single-quotation-mark as a digit separator14.04.916.03.4
C++17核心功能MSVCGCCICCClang替代方案
static_assert with no message15.06No3.5C++11’s static_assert
Disabling trigraph expansion by default12.05.1No3.5
typename in a template template parameter14.0517.03.5
New auto rules for direct-list-initialization14.0517.03.8
Fold expressionsNo6No3.6
Attributes for namespaces and enumerators14.06No3.6
u8 character literals14.0617.03.6
Nested namespace definition15.0617.03.6
Allow constant evaluation for all non-type template argumentsNo6No3.6
Remove deprecated register storage classNo7No3.8
Remove deprecated bool incrementNo7No3.8
Make exception specifications part of the type systemNo7NoNo
__has_include in preprocessor conditionalsNo5NoYes
New specification for inheriting constructors (DR1941 et al)No7No3.9
[[fallthrough]] attribute15.07No3.9
[[nodiscard]] attributeNo7No3.9
[[maybe_unused]] attributeNo7No3.9
Aggregate initialization of classes with base classesNo7No3.9
constexpr lambda expressionsNo7NoNo
Unary Folds and Empty Parameter PacksNo6No3.9
Differing begin and end types in range-based for15.06No3.9
Lambda capture of *thisNo7No3.9
Direct-list-initialization of enumsNo7No3.9
Hexadecimal floating-point literalsNo3.0NoYes
Using attribute namespaces without repetitionNo7No3.9
Dynamic memory allocation for over-aligned dataNo7NoNo
Template argument deduction for class templatesNo7NoNo
Non-type template parameters with auto typeNo7NoNo
Guaranteed copy elisionNo7NoNo
Stricter expression evaluation orderNo7NoNo
Requirement to ignore unknown attributesNoYesNoYes
constexpr if-statementsNo7No3.9
Inline variablesNo7No3.9
Structured bindingsNo7NoNo
Separate variable and condition for if and switchNo7No3.9
Matching template template parameters to compatible argumentsNo7NoNo
Removing deprecated dynamic exception specificationsNo7NoNo
Pack expansions in using-declarationsNoNoNoNo

3. c++11核心功能

3.1 可变参数模板(Variadic Template)

  • 主要增加关键字…, 表示一个组或者包,代表可变的参数个数或类型
  • 从模板函数上来看,可以更好的完成递归函数的调用,具体如代码example1
  • 从类模板的角度看,可以更方便的完成递归继承,具体如代码example2

example1 函数模板

//注意必须配一个无参的print函数,否则递归没有边界
void print()
{
}

//定义一个模板函数 1 + n, 更具化,
//(1)...                    表示一个所谓的包(pack),一组
//(2)typename... Types      模板参数包
//(3)const Types&... args   函数参数类型包
//(4)args...                参数包
template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
{
    std::cout<<"i'm 1+n"<<std::endl;
    std::cout<<firstArg<<std::endl;
    //递归()
    print(args...);
}


//n  更泛化,优先调用更具化的模板函数
template <typename... Types>
void print(const Types&... args)
{
    std::cout<<"i'm n"<<std::endl;
    //递归
    print(args...);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

example2 类模板

//注意,一定要先加这一句
template <typename... Values> class MytestClassTem;
template<> class MytestClassTem<>{};
template<typename First,typename... others>
class MytestClassTem<First, others...>
: private MytestClassTem<others...>
{
    typedef MytestClassTem<others...> inherited;
public:
    MytestClassTem(){}
    MytestClassTem(First v, others... args)
    :m_first(v), inherited(args...){}

    First first(){return m_first;}
    //此处返回的是父类的实例
    inherited& other(){return *this;}
protected:
    First m_first;
};


/调用
    MytestClassTem<int ,float,std::string> obj(41, 6.3, "hello");
    std::cout<<"子级:"<<obj.first()<<std::endl;
    std::cout<<"子级的上一级:"<<obj.other().first()<<std::endl;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

3.2 右尖括号(Right Angle Brackets)

  • 在C++11之前,两个右尖括号之间必须加个空格
#include <vector>
typedef std::vector<std::vector<int> > Table;  // OK
typedef std::vector<std::vector<bool>> Flags;  // Error
  • 1
  • 2
  • 3
  • C++ 11中,加或者不加空格,都可以识别了
#include <vector>
typedef std::vector<std::vector<int> > Table;  // OK
typedef std::vector<std::vector<bool>> Flags;  // OK
  • 1
  • 2
  • 3

3.3 空指针(nullptr)

  • C++11之前没有关键字nullptr(其对应的类型为std::nullptr_t)
  • nullptr用于替换0或者NULL,因为NULL本身是定义为0的,有时候使用会产生歧义
    fun(0);//call fun(int)
    int i  = NULL;
    fun(i);//call fun(int)
    char* pp = NULL;
    fun(pp);//call fun(void*)
    fun(NULL);//有歧义
    fun(nullptr);//call fun(void*)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.4 auto关键字

  • 在C++11中,可以auto定义变量,而不指定变量的类型
  • 编译器会从等号右边的表达式或变量来推断auto变量的类型
  • auto 一般用于替代变量类型名字太长,或表达式类型太复杂的场景
     //简单类型不建议使用auto
     auto data = 2;
    //typeid包含在#include <typeinfo>
    std::cout<<typeid(data).name()<<std::endl;//i --- int

    auto res = f();
    std::cout<<typeid(res).name()<<std::endl;//f  -- float

    
    //auto 一般用于替代变量类型名字太长,或表达式类型太复杂的场景
    std::vector<std::string> vecStr;
    auto itr = vecStr.begin(); //auto取代 std::vector<std::string>::iterator itr = vecStr.begin()
    auto lam = [](int x)->bool{return x = 0;}; // auto取代lambda表达式的类型
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.5 统一初始化(Uniform Initialization)

  • C++11之前,有三种为变量或对象初始化的方式,大括号,小括号, 赋值符号
  • C++ 11 引入统一的初始化方式 ————统一使用大括号,内部编译器会进行转换,编译器会在识别到{t1,t2,…,tn}时,自动保存至一个initializer_list对象中,并关联至一个一个array<T,n>,在调用构造函数时,会将array中的元素逐一分解传递给构造函数
  • 若构造函数的入参为initializer_list时,则不做分解,直接整包传进去
class TestInitializerList
{
public:
    TestInitializerList(int a, int b)
    {
        std::cout<<"TestInitializerList(int,int), a = "<<a<<",b="<<b<<std::endl;
    }

    
    TestInitializerList(std::initializer_list<int> initList)
    {
    
        std::cout<<"TestInitializerList(initializer_list<int> initList) ,vals = ";
        for(auto i : initList)
        {
            std::cout<<i<<",";
        
        }
        std::cout<<std::endl;
        
    }
};


    TestInitializerList a(77,5);//call TestInitializerList(int a, int b)
    TestInitializerList b{65,9};//若包含std::initializer_list<int>的构造函数存在,则调用,如不存在,则逐一分解,调用构造函数TestInitializerList(int a, int b)
    TestInitializerList c{34,56,78};若包含std::initializer_list<int>的构造函数存在,则调用,则分解,分解后若有匹配的构造函数则调用,没有匹配的会报错
    TestInitializerList d={67,90};//同d{65,9};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • C++11也兼容之前的小括号和赋值符号的初始化方式
     before C++11
    //小括号
    Rect r(3,4);
    r.printRect();
    //大括号
    int ia[2] = {3,4};
    //赋值符号
    Rect r1 = {4,5};//在C++11之前只能使用构造函数来对对象初始化
    r1.printRect();

   //C++11 Uniform Initializzation
    int ib[2]{6,7};
    std::vector<int> vec{8,9};
    Rect r2{3,9};
    r2.printRect();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • C++11引入统一的初始化对象或变量的方式,其主要目的是构造对象时可接收可变的参数,在标准库的设计上提供了很大的便利.
  • 引入了std::initializer_list<>模板类,其内部维护了一个std::array数据
    //有了Initializer List后,直接不限个数,#include<algorithm>
    std::cout<<std::max({1,2,3,4,5})<<std::endl;
    //有了Initializer List后,直接不限个数,#include<algorithm>
    std::cout<<std::min({1,2,3,4,5})<<std::endl;
  • 1
  • 2
  • 3
  • 4
  • {}可以为变量设定初值,但不能强制转换类型
int i;//i有一个未定义的值
   std::cout<<i<<std::endl;
   int j{};//j被初始化为0
   std::cout<<j<<std::endl;
   
   char* p;//p有一个未定义的指针值
   std::cout<<p<<std::endl;
   char* q{};//q=nullptr
   std::cout<<q<<std::endl;


    
   int i(5.3);//将浮点型强制转换成整型
   std::cout<<i<<std::endl;

   int j{5.0};//不会将浮点型强制转换成整型
   std::cout<<j<<std::endl;
   
   char p{8};//OK
   std::cout<<p<<std::endl;

   char q{99999};//99999不在char范围内,无法强制转换
   std::cout<<q<<std::endl;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

3.6 关键字explicit

  • explicit含义是明确的,显示的,一般用在构造函数前面,但在C++11之前,只能放在包含一个入参的构造函数前面
struct TestExplicit
{
    explicit TestExplicit(int re, int im = 0):real(re),imag(im)
    {}
    
    TestExplicit operator+(const TestExplicit& x)
    {
    
        return TestExplicit((real+x.real),(imag+x.imag));
    }
    
    int real;
    int imag;

};


    TestExplicit a(12,5);
    std::cout<<a.real<<","<<a.imag<<std::endl;
    //编译器自动调用TestExplicit(int re, int im = 0)将int 5转换为TestExplicit对象
    //若加上explicit ,则调用TestExplicit(int re, int im = 0)时需要更加明确,不能默认将int 转换为则调用TestExplicit
    TestExplicit b = a + 5;
    std::cout<<b.real<<","<<b.imag<<std::endl;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • C++11中允许explicit放在包含多个入参的构造函数前面(其实是基于Uniform Initialization,扩展了其功能)
class TestNewExplicit
{
public:
    TestNewExplicit(int a,int b)
    {
        std::cout<<"TestNewExplicit(int a,int b), a = "<<a<<",b="<<b<<std::endl;
    }
    
    TestNewExplicit(std::initializer_list<int> initList)
    {
        std::cout<<"TestNewExplicit(std::initializer_list<int> initList) ,vals = ";
        for(auto i : initList)
        {
            std::cout<<i<<",";
        
        }
        std::cout<<std::endl;
    }
    
    explicit TestNewExplicit(int a,int b,int c)
    {
        std::cout<<"explicit TestNewExplicit(int a,int b,int c), a = "<<a<<",b="<<b<<",c="<<c<<std::endl;
    }
    
    
};

    TestNewExplicit p1(77,5);
    TestNewExplicit p2{77,5};
    //TestNewExplicit p3{77,5,99,88};
    //若不存在接收std::initializer_list<int>参数的构造函数,则需要编译器自动分解,匹配个数进行调用相应参数的构造函数
    //若分解匹配后的构造函数前面加了explicit,则不能直接调用
    //converting to 'TestNewExplicit' from initializer list to TestNewExplicit
    TestNewExplicit p4 = {1,2,3};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

3.7 基于范围的for循环(range-based for statement)

  • 从容器中依次取出元素进行处理
  • 接收者为引用时,则用一个指针指向容器中的元素,不需要拷贝一份
    for(int i : {1,2,3,4})
    {
        std::cout<<i<<std::endl;
    
    }
    
    std::vector<int> vec{4,5,6};
    for(auto& j : vec)
    {
        std::cout<<j<<std::endl;
        std::cout<<"j的地址:"<<&j<<",vec[0]的地址"<<&vec[0]<<std::endl;
    
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.8 =default/=delete

  • Big-Three:表示类中默认的析构函数,拷贝构造函数,operator=(C++11中引入右值引用后,增加了移动拷贝构造函数,移动operator=, 被称为Big-Five),这些函数不能被重载
  • =default ,只能被用在Big-Five函数后面,表示使用编译器默认的函数
  • =delete, 一般用在Big-Five函数后面,但也可以使用在其他普通函数函数后面,表示告知编译器不要定义该函数(建议不要用在析构函数上)
class Test
{
public:
    //不能使用编译器默认的构造函数
    Test() = delete;

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

class Zoo
{
public:
    //自定义构造函数
    Zoo(int i1,int i2):d1(i1),d2(i2)
    {}
    //拷贝构造函数,被删除
    Zoo(const Zoo&) = delete;
    //移动构造函数  使用编译器默认的
    Zoo(Zoo&&) = default;
    //赋值运算符   使用编译器默认的
    Zoo& operator=(const Zoo&) = default;
    //移动赋值运算符   被删除
    Zoo& operator=(const Zoo&&) = delete;
    virtual ~Zoo(){}
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

附:经典网站及书籍

名称说明
C++14标准PDFhttps://doc.imzlp.me/viewer.html?file=docs/standard/isocpp2014.pdf
C++11标准PDFhttps://doc.imzlp.me/viewer.html?file=docs/standard/isoc11.pdf
多种编译器对C++11的支持http://www.klayge.org/wiki/index.php/多种编译器对C%2B%2B11的支持
多种编译器对C++14的支持http://www.klayge.org/wiki/index.php/多种编译器对C%2B%2B14的支持
多种编译器对C++17的支持http://www.klayge.org/wiki/index.php/多种编译器对C%2B%2B17的支持
在线开发环境https://wandbox.org/
各VS版本对C++11功能的支持https://blog.csdn.net/xiaomu_347/article/details/82563688
侯捷老师的《C++11新特性》视频https://www.bilibili.com/video/av51863195/?p=9
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/732337
推荐阅读
相关标签
  

闽ICP备14008679号