新特性之C++17
目录
u8字符字面量
noexcept作为类型系统的一部分
Lambda表达式捕获*this
constexpr新特性
编译期的if constexpr语句
constexpr的Lambda表达式
变量新特性
inline变量
结构化绑定
if和switch语句中的初始化器
强制的复制省略(返回值优化)
临时物质化
模板新特性
折叠表达式(…)
类模板实参推导
auto占位的非类型模板形参
u8字符字面量
C++17 引入了u8字符字面量,用于表示 UTF-8 编码的字符串。
auto str = u8"Hello, 世界";
noexcept作为类型系统的一部分
noexcept是 C++11 引入的一个关键字,用于改善C++中异常处理的性能和可用性。noexcept指定的函数保证不会抛出异常,这使得编译器有机会进行优化,同时也为程序员提供了一个清晰的工具来指明哪些函数是安全的,即不会因为异常而失败。
C++17对其改动主要如下:
- 更广泛的使用
C++17 标准库在很多已有的函数中增加了noexcept说明。这是因为对异常安全性有更高的要求和对性能优化的关注。比如,在移动语义和智能指针等方面,更频繁地看到noexcept的使用。
- 推导规则
C++17 引入了新的推导指南,使得noexcept能够在模板和自动类型推导中得到更好的处理。这包括在模板函数和自动返回类型中,noexcept的状态可以被推导出来。例如,一个模板函数可以根据其模板参数的操作是否不抛出异常,来决定自身是否声明为noexcept。
- 移动操作的默认noexcept
在 C++11 和 C++14 中,移动构造函数和移动赋值操作不会自动被推断为noexcept,而在 C++17 中,如果一个类的所有成员和基类的移动构造函数和移动赋值操作都是noexcept的,那么这个类的移动操作也会被推断为noexcept。这改善了容器(如 std::vector)在元素类型是可移动但不抛出异常时的性能,因为容器可以安全地进行更优化的内存操作,如使用realloc而不是手动复制。
- 对std::swap的优化
C++17 中,std::swap在可能的情况下使用noexcept来确保不抛出异常,这对于某些类型来说,特别是在模板编程中,可以提高效率和安全性。
Lambda表达式捕获*this
C++17的Lambda引入捕获*this,使得可以捕获当前对象的常量副本,相当于是以值捕获的形式捕获了this指向的对象,并且赋予const属性。
class A
{
int a = 1;
public:
void printCopyA()
{
auto lambda = [*this] {
a++; // error C3490: 由于正在通过常量对象访问“a”,因此无法对其进行修改
std::cout
auto lambda = [&] {
a++;
std::cout
A a;
a.printA();
a.printCopyA();
}
if constexpr (std::is_integral_v
std::cout
std::cout
std::cout
process(10); // 输出:Integral type with value: 10
process(3.14); // 输出:Floating-point type with value: 3.14
process("Hello"); // 输出:Other type
}
if constexpr (std::is_integral_v
std::cout
std::cout
nonExistentFunc(value); // 不会被编译
std::cout
process(10); // 输出:Integral type with value: 10
process(3.14); // 输出:Floating-point type with value: 3.14
}
return a + b; };
static_assert(add(2, 3) == 5);
return {42, 3.14, "Hello"};
}
int main() {
auto [a, b, c] = getTuple();
std::cout
int x, y;
};
int main() {
Point p{10, 20};
auto [x, y] = p;
std::cout
int arr[] = {1, 2, 3};
auto [a, b, c] = arr;
std::cout
std::map{"Alice", 5}, {"Bob", 10}};
if (auto it = myMap.find("Alice"); it != myMap.end()) {
std::cout
std::cout
std::vector10, 20, 30, 40};
switch (auto i = numbers.size(); i) {
case 4:
std::cout
public:
std::vector
std::cout
std::cout
std::cout
BigObject obj;
obj.data.resize(1000); // 假设是一个资源密集型操作
return obj;
}
int main() {
BigObject myObj = createBigObject();
// 应该看不到复制或移动构造函数的调用信息
return 0;
}
int value;
A(int v) : value(v) { std::cout std::cout
return A(5); // 返回 prvalue
}
void takeA(A a) {
std::cout
const A& aRef = A(10); // prvalue 物质化为临时对象,引用绑定到它
takeA(A(20)); // prvalue 物质化为临时对象,传递给函数
A myA = getA(); // prvalue 物质化过程
return 0;
}
return (args + ... + std::string("")); // 一元右折叠
}
int main() {
std::cout
return (std::string("") + ... + args); // 一元左折叠
}
int main() {
std::cout
return (args + ... + init); // 二元右折叠
}
int main() {
std::cout
return (init + ... + args); // 二元左折叠
}
int main() {
std::cout 1, 2, 3};
std::pair1, 3.14};
1, 2, 3}; // 推导为 std::vector1, 3.14}; // 推导为 std::pair
};
int main()
{
std::vector1, 2, 3};
// 使用例子
Constant