【C++11】新特性

简介: `C++11`是2011年发布的`C++`重要版本,引入了约140个新特性和600个缺陷修复。其中,列表初始化(List Initialization)提供了一种更统一、更灵活和更安全的初始化方式,支持内置类型和满足特定条件的自定义类型。此外,`C++11`还引入了`auto`关键字用于自动类型推导,简化了复杂类型的声明,提高了代码的可读性和可维护性。`decltype`则用于根据表达式推导类型,增强了编译时类型检查的能力,特别适用于模板和泛型编程。

前言:

C++11C++编程语言的一个重要版本,于2011年发布。它带来了数量可观的变化,包含约 140个新特性,以及对C++03 标准中约600个缺陷的修正,更像是从 C++98/03 中孕育出的新语言

列表初始化

C++11中的列表初始化(List Initialization)是一种新的初始化语法,它提供了更统一、更灵活和更安全的初始化方式。以下是关于 C++11 列表初始化的详细介绍

基本语法

可以直接在变量名后面加上初始化列表来进行对象的初始化。

int x = {
   10};  // 初始化一个整数变量 x 为 10
int arr[] = {
   1, 2, 3};  // 初始化一个整数数组 arr
std::vector<int> v = {
   4, 5, 6};  // 初始化一个 std::vector 容器

适用范围

  • 内置类型:可用于所有内置类型,如整数、浮点数、字符等。
  • 自定义类型:对于自定义的类或结构体,如果满足聚合类型的条件,也可以使用列表初始化。如果类定义了合适的构造函数(包括接受 std::initializer_list 类型参数的构造函数),同样可以使用列表初始化

聚合类型的条件

在 C++11 中,聚合类型需要满足以下条件1:

  • 类型是一个普通数组:如 int[5]char[]double[3] 等。
  • 类型是一个类,且满足以下条件:
    • 没有用户声明的构造函数。
    • 没有用户提供的构造函数(允许显示预置或弃置的构造函数)。
    • 没有私有或保护的非静态数据成员。
    • 没有基类。
    • 没有虚函数。
    • 没有 {}= 直接初始化的非静态数据成员。
    • 没有默认成员初始化器

initializer_list

initializer_listC++11 引入的一种模板类,用于表示某种类型的对象的列表。它提供了一种方便的方式来处理和传递一组相同类型的值,类似于其他语言中的列表或数组。以下是关于 initializer_list 的一些重要特点和用法:

  • 创建和初始化

    • 可以使用花括号 {} 来创建一个 std::initializer_list 对象,并在其中列出要包含的元素。例如:std::initializer_list<int> myList = {1, 2, 3, 4, 5};
    • 元素的类型必须相同,否则会导致编译错误。不过,编译器会进行一些隐式的类型转换,例如将 int 类型的元素转换为 double 类型的列表是允许的,但可能会有精度损失。例如:std::initializer_list<double> doubleList = {1, 2, 3}; 这里 123 会被隐式转换为 1.02.03.0
    • 不允许进行缩窄转换,即会导致数据丢失或精度降低的转换是不被允许的。例如:std::initializer_list<char> charList = {1000}; 会导致编译错误,因为 1000 超出了 char 类型的取值范围。
  • 常见用法

    • 初始化容器:可以方便地用于初始化标准库中的容器,如 std::vectorstd::list 等。例如:std::vector<int> vec = {1, 2, 3};
    • 函数参数:函数可以接受 std::initializer_list 作为参数,从而可以接收任意数量的同类型参数。例如:
    void printValues(std::initializer_list<int> values) {
         
        for (auto value : values) {
         
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }
    
    int main() {
         
        printValues({
         1, 2, 3, 4, 5});
        return 0;
    }
    
    • 类的构造函数:在类的构造函数中使用 std::initializer_list,可以方便地实现多种初始化方式。例如:

auto

C++11 中,auto是一个用于自动类型推导的关键字。它可以让编译器根据初始化表达式的类型自动推断变量的类型,从而简化代码的编写,提高代码的可读性和可维护性1。以下是关于 auto 的一些重要特点和用法:

  • 基本用法:在定义变量时,使用 auto 关键字,编译器会根据初始化的值来推断变量的类型。例如:
auto x = 5;  // x 的类型为 int
auto y = 3.14;  // y 的类型为 double
auto z = "hello, world!";  // z 的类型为 const char*

与指针和引用结合

  • auto 可以与指针结合使用,用于自动推导指针类型。例如:
int num = 10;
auto ptr = &num;  // ptr 的类型为 int*
  • 用于函数返回值类型推导(C++14):在 C++14 标准中,可以使用 auto 关键字结合 -> 运算符来推导函数的返回值类型。例如:
auto add(int a, int b) -> int {
   
    return a + b;  // 返回 a 和 b 的和
}
  • 在范围 for 循环中的应用(C++11):C++11 引入的范围 for 循环结合 auto 关键字,可以更方便地遍历容器。例如:
std::vector<int> numbers = {
   1, 2, 3, 4, 5};
for (auto number : numbers) {
   
    std::cout << number << std::endl;  // 输出每个元素
}

优点

  • 简化代码:避免了手动指定复杂的类型,尤其是对于模板库中复杂的迭代器类型等,使用 auto 可以大大简化代码的书写1。
  • 提高可读性:使代码更加清晰易读,减少了类型声明带来的视觉干扰,让开发者更关注于代码的逻辑而不是类型的细节1。
  • 灵活性:在类型需要改变时,只需要修改初始化表达式的类型,而使用 auto 定义的变量的类型会自动随之改变,减少了代码修改的工作量。

decltype

基本语法

decltype(expression) variable_name;
  • expression:用于推导类型的表达式。
  • variable_name:根据推导出的类型声明的变量名。

使用场景

  1. 推导变量的类型
    通过 decltype,你可以推导一个已有变量的类型:

    int a = 5;
    decltype(a) b = 10; // b的类型是int,和a相同
    
  2. 推导表达式的类型
    decltype 也可以用于推导更复杂的表达式的类型:

    int x = 10;
    decltype(x + 1.0) y;  // y的类型是double,因为x+1.0的结果是double
    
  3. 用于函数返回类型
    在C++11中,你可以通过 decltype 根据函数内部表达式来指定返回类型:

    template<typename T1, typename T2>
    auto add(T1 a, T2 b) -> decltype(a + b) {
         
        return a + b;
    }
    

    这里,decltype(a + b) 将推导出 a + b 的类型,并用作返回类型。

  4. 在lambda表达式中使用
    在C++11中,你可以在lambda表达式中使用 decltype 推导捕获变量或返回值的类型:

    auto lambda = [](int x, int y) -> decltype(x + y) {
         
        return x + y;
    };
    

decltype与auto的区别

  • auto 用于根据初始化表达式推导变量类型。
  • decltype 不需要初始化,直接根据表达式推导类型,可以用于声明变量、函数返回类型等。

例如:

int a = 5;
auto b = a;  // auto推导b为int
decltype(a) c;  // decltype推导c为int,但c未初始化

用于根据初始化表达式推导变量类型。

  • decltype 不需要初始化,直接根据表达式推导类型,可以用于声明变量、函数返回类型等。

例如:

int a = 5;
auto b = a;  // auto推导b为int
decltype(a) c;  // decltype推导c为int,但c未初始化

decltype 可以在编译时推导任意表达式的类型,因此在模板和泛型编程中,decltype 是一个强大的工具。

目录
相关文章
|
2月前
|
编译器 程序员 定位技术
C++ 20新特性之Concepts
在C++ 20之前,我们在编写泛型代码时,模板参数的约束往往通过复杂的SFINAE(Substitution Failure Is Not An Error)策略或繁琐的Traits类来实现。这不仅难以阅读,也非常容易出错,导致很多程序员在提及泛型编程时,总是心有余悸、脊背发凉。 在没有引入Concepts之前,我们只能依靠经验和技巧来解读编译器给出的错误信息,很容易陷入“类型迷路”。这就好比在没有GPS导航的年代,我们依靠复杂的地图和模糊的方向指示去一个陌生的地点,很容易迷路。而Concepts的引入,就像是给C++的模板系统安装了一个GPS导航仪
134 59
|
2月前
|
存储 编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(三)
【C++】面向对象编程的三大特性:深入解析多态机制
|
2月前
|
存储 编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(二)
【C++】面向对象编程的三大特性:深入解析多态机制
|
2月前
|
编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(一)
【C++】面向对象编程的三大特性:深入解析多态机制
|
2月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
2月前
|
C++
C++ 20新特性之结构化绑定
在C++ 20出现之前,当我们需要访问一个结构体或类的多个成员时,通常使用.或->操作符。对于复杂的数据结构,这种访问方式往往会显得冗长,也难以理解。C++ 20中引入的结构化绑定允许我们直接从一个聚合类型(比如:tuple、struct、class等)中提取出多个成员,并为它们分别命名。这一特性大大简化了对复杂数据结构的访问方式,使代码更加清晰、易读。
44 0
|
3月前
|
编译器 C++ 计算机视觉
C++ 11新特性之完美转发
C++ 11新特性之完美转发
58 4
|
3月前
|
Java C# C++
C++ 11新特性之语法甜点1
C++ 11新特性之语法甜点1
37 4
|
3月前
|
安全 程序员 编译器
C++ 11新特性之auto和decltype
C++ 11新特性之auto和decltype
48 3
|
3月前
|
编译器 C++ 容器
C++ 11新特性之语法甜点2
C++ 11新特性之语法甜点2
34 1