🔥个人主页:Quitecoder
🔥专栏:c++笔记仓
相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多
在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定:
struct Point
{
int _x;
int _y;
};
int main()
{
int array1[] = { 1, 2, 3, 4, 5 };
int array2[5] = { 0 };
Point p = { 1, 2 };
return 0;
}
C++11扩大了用大括号括起的列表的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用列表初始化时,可添加等号(=),也可不添加
struct Point
{
int _x;
int _y;
};
int main()
{
int x1 = 1;
int x2{ 2 };
int array1[]{ 1, 2, 3, 4, 5 };
int array2[5]{ 0 };
Point p{ 1, 2 };
// C++11中列表初始化也可以适用于new表达式中
int* pa = new int[4] { 0 };
return 0;
}
创建对象时也可以使用列表初始化方式调用构造函数初始化
class Date
{
public:
Date(int year, int month, int day)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int year, int month, int day)" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2025, 1, 1); // old style
// C++11支持的列表初始化,这里会调用构造函数初始化
Date d2{ 2025, 1, 2 };
Date d3 = { 2025, 1, 3 };
return 0;
}
std::initializer_list
是 C++11 引入的一个标准库类模板,用于支持统一的列表初始化语法,并简化初始化列表的处理。它常用于用户自定义的类或函数,使其可以接受固定数量的初始化元素。
1. 定义与特点
std::initializer_list
的定义位于头文件 <initializer_list>
中。它的主要特点包括:
initializer_list
对象。2. 主要功能
std::initializer_list
提供以下功能:
begin()
和 end()
,便于访问底层元素。size()
获取元素个数。3. 使用示例
(1)用于函数参数
一个函数可以接受 std::initializer_list
参数,从而支持传入多个值作为初始化列表:
#include <initializer_list>
#include <iostream>
void print(std::initializer_list<int> values) {
for (int value : values) {
std::cout << value << " ";
}
std::cout << std::endl;
}
int main() {
print({1, 2, 3, 4, 5}); // 传入一个初始化列表
print({10, 20}); // 传入另一个初始化列表
return 0;
}
输出:
1 2 3 4 5
10 20
(2)用于类构造函数
可以使用 std::initializer_list
构造一个类,使其接受列表初始化:
#include <initializer_list>
#include <iostream>
#include <vector>
class MyContainer {
public:
MyContainer(std::initializer_list<int> values) {
for (int value : values) {
_data.push_back(value);
}
}
void print() const {
for (int value : _data) {
std::cout << value << " ";
}
std::cout << std::endl;
}
private:
std::vector<int> _data;
};
int main() {
MyContainer container{1, 2, 3, 4, 5};
container.print();
return 0;
}
输出:
1 2 3 4 5
(3)与标准容器结合
可以直接使用 std::initializer_list
初始化标准容器:
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5}; // 使用初始化列表初始化 std::vector
for (int value : v) {
std::cout << value << " ";
}
std::cout << std::endl;
return 0;
}
输出:
1 2 3 4 5
4. std::initializer_list
的内部实现
std::initializer_list
实际上是一个轻量级的类模板,内部包含一个指针和大小信息:
template<class T>
class initializer_list {
public:
// 类型定义
using value_type = T;
using reference = const T&;
using const_reference = const T&;
using size_type = size_t;
// 获取指针指向的第一个元素
constexpr const T* begin() const noexcept { return _array; }
// 获取指针指向的最后一个元素的下一个位置
constexpr const T* end() const noexcept { return _array + _size; }
// 获取元素数量
constexpr size_t size() const noexcept { return _size; }
private:
// 构造时由编译器自动设置
const T* _array;
size_t _size;
};
由此可以看出:
5. 注意事项
std::initializer_list
中的内容是常量,不能被修改。std::vector
)。std::initializer_list
对象,必须通过 {}
初始化。// 这里{"sort", "排序"}会先初始化构造一个pair对象
map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
c++11提供了多种简化声明的方式,尤其是在使用模板时
在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型
int main()
{
int i = 10;
auto p = &i;
auto pf = strcpy;
cout << typeid(p).name() << endl;
cout << typeid(pf).name() << endl;
map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
//map<string, string>::iterator it = dict.begin();
auto it = dict.begin();
return 0;
}
decltype
是一个非常强大的工具,尤其在泛型编程和模板编程中,它为类型推导提供了灵活性。它的能力远超 auto
,可以精确地获取表达式的类型,尤其适合处理复杂类型和左值/右值差异的场景。
1. 定义与作用
decltype
的功能是检查一个表达式的类型,并返回该类型。
其语法形式为:
decltype(expression)
编译器在编译时会评估 expression
,但不会实际计算它,只会确定其类型。
2. 使用场景与示例
(1)推导变量类型
decltype
可以用来推导变量的类型,而无需显式声明。
#include <iostream>
int main() {
int a = 42;
decltype(a) b = 10; // b 的类型与 a 相同,即 int
std::cout << b << std::endl; // 输出 10
return 0;
}
(2)与 auto
配合使用
虽然 auto
也能推导类型,但有时需要明确指定某个表达式的类型,decltype
更适合。
#include <iostream>
int main() {
int x = 10;
double y = 3.14;
auto z = x + y; // z 的类型为 double,因为表达式 x + y 的结果是 double
decltype(x + y) w = 5.5; // 显式指定 w 的类型与 x + y 的类型一致,即 double
std::cout << w << std::endl; // 输出 5.5
return 0;
}
(3)用在函数返回类型
在函数返回类型中,可以使用 decltype
推导返回值的类型,特别是在泛型编程中非常有用。
#include <iostream>
int add(int a, int b) {
return a + b;
}
template <typename T1, typename T2>
auto multiply(T1 a, T2 b) -> decltype(a * b) { // 返回类型由 a * b 的类型决定
return a * b;
}
int main() {
std::cout << multiply(2, 3.5) << std::endl; // 输出 7
return 0;
}
(4)获取成员变量类型
decltype
可用于获取类成员变量的类型。
#include <iostream>
class MyClass {
public:
int value;
};
int main() {
MyClass obj;
decltype(obj.value) x = 42; // x 的类型与 obj.value 相同,即 int
std::cout << x << std::endl; // 输出 42
return 0;
}
(5)推导复杂表达式的类型
decltype
可用于推导复杂表达式的类型。
#include <iostream>
int main() {
int a = 10, b = 20;
decltype(a + b) sum = a + b; // sum 的类型为 int,因为 a + b 是 int
decltype((a)) ref_a = a; // ref_a 的类型为 int&,因为 (a) 是左值表达式
++ref_a; // 修改 ref_a 会影响 a
std::cout << a << " " << sum << std::endl; // 输出 11 30
return 0;
}
注意:
decltype
会推导为引用类型。例如 decltype((a))
是 int&
。decltype
推导为普通值类型。3. 与 auto
的区别
特性 | auto | decltype |
---|---|---|
用法 | 推导变量的类型 | 推导表达式的类型 |
推导方式 | 推导为值类型 | 区分左值和右值,左值推导为引用类型 |
示例 | auto x = a; | decltype(a) x = a; |
适用场景 | 用于声明变量并自动推导类型 | 用于获取任意表达式的类型 |
4. 注意事项
decltype((a))
是左值表达式,推导为引用类型。decltype(a)
是右值表达式,推导为值类型。auto
混淆:
auto
用于声明变量,不能直接用于表达式类型推导。decltype
专注于类型检查和推导。