前言:
C++11
是C++
编程语言的一个重要版本,于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_list
是 C++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};
这里1
、2
、3
会被隐式转换为1.0
、2.0
、3.0
。 - 不允许进行缩窄转换,即会导致数据丢失或精度降低的转换是不被允许的。例如:
std::initializer_list<char> charList = {1000};
会导致编译错误,因为1000
超出了char
类型的取值范围。
- 可以使用花括号
-
常见用法:
- 初始化容器:可以方便地用于初始化标准库中的容器,如
std::vector
、std::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 = # // 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
:根据推导出的类型声明的变量名。
使用场景
-
推导变量的类型
通过decltype
,你可以推导一个已有变量的类型:int a = 5; decltype(a) b = 10; // b的类型是int,和a相同
-
推导表达式的类型
decltype
也可以用于推导更复杂的表达式的类型:int x = 10; decltype(x + 1.0) y; // y的类型是double,因为x+1.0的结果是double
-
用于函数返回类型
在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
的类型,并用作返回类型。 -
在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
是一个强大的工具。