【C++11入门】新特性总结大全-Part2
【C++11入门】新特性总结大全-Part1-CSDN博客
上一篇我们提了一些我们常用但你可能不知道是C++11标准的一些特性。本节开始对上一节的内容进行一定的延伸。
1.auto与decltype
我们知道auto可以代替一些数据类型对变量进行定义,根据初始值自动给出最适数据类型。有时我们会遇到这样的情况:希望从表达式的类型推断出要定义的变量的类型,但是不详用该表达式的值来初始化变量。为了满足这一要求,C++11新标准引入了auto外的第二种类型说明符decltype。
它的作用是选择并返回操作数的数据类型。在此过程中,编译器分析表达式并得到他的类型。却不实际计算表达式的值:
decltype(expression) variable;
其中 decltype后面的表达式可以是变量、函数、普通表达式。
T func(){/**/};
int a = 5;
double b = 3.14;
decltype(a) cp_I;//cp_I 是a的类型int
decltype(a*b) cp_D;//cp_D 是a*b的结果的类型double类型
decltype(func()) cp_T//cp_T 是 func()的返回值类型 T 类型。
在此之前我们有一个typeid().name();它可以将类型推断出来然后使用name()返回类型的字符串形式,但是它无法作为类型说明符对变量进行定义等操作。所以decltype的引入,带来了极大的方便。
delctype与引用
下面给出decltype与引用的专题讲解:
①decltype处理顶层const和引用的方式和auto有所不同,如果decltype使用的表达式是一个变量,则decltype返回该变量的类型,包括顶层const和引用在内:
const int ci=0, &cj =ci;
decltype(ci) x=0;//x的类型就是const int
decltype(cj) y=x;//y的类型就是const int&,y绑定到变量x
decltype(cj) z; //err,z是一个引用,必须初始化。
因为cj是一个引用,decltype(cj)的结果就是引用类型。需要指出的是:引用从来都是作为其所指对象的同义词出现,只有用在decltype处是一个例外。
有些表达式向decltype返回一个引用类型。一般来说当这种情况发生时,意味着该表达式的结果对象能作为一条赋值语句的左值:
int i=42, *p=&i, &r=i;
decltype(r+0) b;//正确:加法的结果是int 因此b是一个未初始化的int
decltype(*p) c;//错误,c是int& ,必须初始化
②因为r是一个引用,因此decltype(r)是一个引用类型。如果想结果是r所指的类型,可以把r作为表达式的一部分,如r+0,显然这个表达式的结果是一个int类型的具体值,而非一个引用。
③另一方面,如果表达式的内容是解引用操作,则decltype将得到引用类型。正如我们所熟悉的那样,解引用指针可以得到指针所指的对象,而且还能给这个对象赋值。因此,decltype(*p)的结果类型是int&,而非int。
④最后一点重要说明:decltype与auto的另一处重要区别是,decltype的结果类型与表达式形式密切相关。有一种情况需要特别注意:对于decltype所用的表达式来说,如果变量名加上了一对括号,则得到的类型与不加括号时会有不同。如果decltype使用的是一个不加括号的变量,则的打斗二结果就是该变量的类型;如果加上了一层或多层括号,编译器会把他当成一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,所以这样decltype就会得到引用类型:
int i=l;
decltype(i) e;//e是一个未初始化的int变量
decltype((i)) d;//err,d是int&,必须初始化
总结:
decltype(xxxx)推到结果是引用的情况
①xxxx是解引用,例如xxxx <- *p。
②xxxx是引用,例如int r&=i; xxxx <- r。
③xxxx是括号括起来的变量,例如,int i=0;xxxx <- (i)。
decltype
是一个强大的工具,可以帮助开发者在不显式地写出类型的情况下,获取变量或表达式的类型。它在模板编程和泛型编程中特别有用,因为它允许编译器根据上下文自动推导类型。
2.列表初始化 与 initializer_list
在C++中,列表初始化和std::initializer_list
是两个相关但不同的概念。下面是对它们的详细解释:
列表初始化
列表初始化是C++11引入的一种新的初始化方式。它使用花括号 {}
来初始化对象,可以用于基本数据类型、用户定义类型(如类和结构体)以及容器等。列表初始化具有以下特点:
直接初始化:可以使用花括号直接初始化变量。
int x{42}; // 列表初始化
vector<int> vec{1, 2, 3}; // 初始化一个 vector
避免窄化:列表初始化不允许从较大类型转换为较小类型(窄化)。
double d{3}; // 合法
int i{3.14}; // 错误,窄化初始化
结构体和类:可以用来初始化结构体和类的成员。
struct Point { int x; int y; };
Point p{1, 2}; // 初始化结构体
std::initializer_list
std::initializer_list
是一个用于支持列表初始化的类型,允许将一组元素传递给构造函数或函数。这种类型提供了便利,特别是在接收多个参数时。例如:
构造函数:
class MyClass {
public:
MyClass(std::initializer_list<int> list) {
// 可以使用 list 来处理初始化数据
}
};
MyClass obj{1, 2, 3}; // 使用 std::initializer_list 来初始化
访问元素:std::initializer_list
提供了 begin()
和 end()
方法,使得可以使用范围基于的循环等操作。
void printList(std::initializer_list<int> list) {
for (int value : list) {
std::cout << value << " ";
}
}
printList({1, 2, 3, 4}); // 可以直接传入 initializer_list
总结:
1.列表初始化 通过花括号
{}
语法来初始化对象,具有避免窄化等特性。
2.std::initializer_list
是一种特殊的类型,允许构造函数或函数接收一组初始化数据,背后实际上利用了列表初始化的特性。3.这两者可以结合使用。例如,使用
std::initializer_list
作为参数的构造函数能够方便地接收列表初始化中的多个元素。
3.const 与 constexpr
const本身不属于C++11的新特性,但其延伸而来的constexpr标识符是C++11的重要特性。所以本节我们可能以const为对比,但着重谈后者。
在C++11中,引入了constexpr
关键字,该关键字用于指示某个函数或变量可以在编译时进行求值。这是为了提高性能和代码优化,尤其是在需要常量表达式的上下文中。下面是对constexpr
的详细解释:
常量表达式:
constexpr
可以用于定义常量表达式,这意味着表达式在编译时就被计算出来,而不需要在运行时计算。
constexpr int square(int x) {
return x * x;
}
constexpr int result = square(5); // 在编译时计算,result 会被初始化为 25
变量:
constexpr
也可以用于变量的定义,确保该变量在编译时具有常量值。
constexpr int max_value = 100; // max_value 是一个编译时常量
类成员函数:
C++11 允许在类中定义 constexpr
成员函数,使得可以在编译时使用这些函数。注意,constexpr
成员函数必须只包含一个返回语句的表达式。
class Point {
public:
constexpr Point(int x, int y) : x_(x), y_(y) {}
constexpr int getX() const { return x_; }
constexpr int getY() const { return y_; }
private:
int x_, y_;
};
constexpr Point p(1, 2);
constexpr int x = p.getX(); // x 在编译时就会被计算
注意事项:
限制:在C++11中,
constexpr
函数的内容有限制,只能是具有单一return
语句的简单表达式。不能有条件语句或循环(这些在C++14及以后可以使用)。使用场景:在需要在编译时已知常量值的情况下,
constexpr
非常有用,如数组大小、模板参数等。稳定性和优化:使用
constexpr
可以提高代码的稳定性和性能,因为编译器能够在编译时进行优化。
C++14和C++17的改进:在C++14及以后的版本中,constexpr
的功能进行了扩展,使得可以在更复杂的上下文中使用,比如支持条件语句、循环等,使得constexpr
函数的能力大大增强。
#include <iostream>
constexpr int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int fact5 = factorial(5); // 计算 5! 在编译时
std::cout << "5! = " << fact5 << std::endl; // 输出 5! = 120
return 0;
}
感谢大家!