一、constexpr if:編譯時條件分支
- 作用:在模板編程中,根據條件在編譯時選擇不同的代碼路徑,無需特化版本或復雜SFINAE技巧[替代SFINAE]。[SFINAE將在模版元編程再講。下個月了。]
注意:默認使用了隱式inline
- 基本語法
if constexpr (condition) {// 如果 condition 為 true,編譯這部分
} else {// 如果 condition 為 false,編譯這部分(可選)
}
- condition 必須是編譯時可求值的常量表達式(如 constexpr 變量、模板參數、sizeof 等)
- 關鍵區別:與普通 if 不同,if constexpr 在編譯時直接丟棄未選擇的分支,不會檢查語法有效性。
二、inline變量:頭文件中的全局/靜態變量定義
- 作用:允許在頭文件中定義全局變量或類的靜態成員變量,避免多次定義的鏈接錯誤。
- inline可以減少函數調用開銷,提高性能。
注意:在類定義內部直接實現的成員函數(如頭文件中的類方法)默認是 inline 的,無需顯式聲明。
- c++17前,只能在頭文件中聲明后,cpp文件中使用。
- extern可以用來告知已經定義過這個變量了。推薦這篇文章
- 例子(c++17前的static 變量,需要再類的外面定義,類里面聲明)
// MyClass.h
class MyClass {
public:static int sharedValue; // 頭文件中聲明
};// MyClass.cpp
int MyClass::sharedValue = 10; // 必須在一個.cpp文件中定義
- 而c++17后,就只需要在static變量前面加inline,就可以定義了。
// MyClass.h (C++17后)
class MyClass {
public:inline static int sharedValue = 10; // 直接初始化,無需外部定義
};// 使用:多個.cpp文件可以安全包含此頭文件
三、類模版參數推導
- 作用:編譯器根據構造函數參數自動推導類模板類型,簡化代碼。
#include <vector>
#include <tuple>// 標準庫的CTAD:無需顯式模板參數
std::pair p(1, 3.14); // 推導為 std::pair<int, double>
std::vector v{1, 2, 3}; // 推導為 std::vector<int>// 自定義類
template <typename T, typename U>
struct MyPair {T first;U second;MyPair(T f, U s) : first(f), second(s) {}
};// 使用CTAD
MyPair mp(5, "hello"); // 推導為 MyPair<int, const char*>// 若構造函數無法推斷類型,可添加推導指引:
template <typename T>
MyPair(T, T) -> MyPair<T, T>; // 處理MyPair(2,3)到MyPair<int, int>的推導
補充:auto占位的非類型模版形參
- auto占位的非類型模版形參
template<auto T>
void func1(){cout<<T<<endl;
}
int main(){func1<100>();//100return 0;
}
四、lambda的this捕獲[*this]
- 作用:按值捕獲當前對象的副本,避免捕獲this指針可能導致的對象銷毀后的懸垂引用。
懸垂引用是指引用了一個已經被銷毀或無效的內存的引用變量。
// C++17后:按值捕獲對象副本,安全
#include <iostream>class Worker {
public:int data = 42;void start() {// C++17前:按引用捕獲this,危險!(若對象銷毀后lambda還在運行)auto lambda_old = [this]() { std::cout << data << "\n"; // 可能的懸垂引用};// C++17后:按值捕獲對象副本,安全auto lambda_new = [*this]() mutable { data++; // 操作的是副本的datastd::cout << data << "\n"; // 輸出43};}
};
五、初始化列表
1、c++11
- 統一初始化語法:允許用花括號 {} 初始化幾乎所有類型的對象,避免舊的 () 和 {} 混亂。
// 常見初始化方式
int arr[] = {1, 2, 3};
std::vector<int> vec = {4, 5, 6};// 聚合類(沒有自定義構造函數、無私有成員等)
struct Point { int x; int y; };
Point p = {10, 20}; // C++11允許聚合初始化// 構造函數使用初始化列表
class Widget {
public:Widget(std::initializer_list<int> list) { /*...*/ }
};
Widget w{1, 2, 3}; // 調用初始化列表構造函數
- 注意特性:
- 禁止窄化轉換(如 double → int):int x{3.14}; 會報錯。
- 解決構造函數歧義:優先匹配 std::initializer_list 構造函數。
- 支持聚合類型初始化:簡化結構體和數組的初始化。
2、c++17初始化列表增強
2.1、類模板參數推導(CTAD)支持初始化列表
// C++17前:需顯式指定模板參數
std::vector<int> v1 = {1, 2, 3};// C++17允許推導
std::vector v2{1, 2, 3}; // 推導為 vector<int>
std::pair p{42, "hello"}; // 推導為 pair<int, const char*>
2.1、聚合初始化擴展
- 允許繼承的聚合初始化:基類可以是聚合類型。
struct Base { int a; };
struct Derived : Base { int b; };
Derived d{{1}, 2}; // C++17:基類成員先初始化
注意:基類成員先初始化
2.2、允許直接列表初始化枚舉
enum class Color { Red, Green, Blue };
Color c{1}; // C++17允許,對應Color::Green
六、namespace的嵌套
- 在c++17前,聲明namespace嵌套,是一層一層的命令。
//C++17之前
namespace A {namespace B {namespace C {void func1() {}} //namespace C} //namespace B
} //namespace A
- c++17是可以使用作用域限定符方式簡化。
//C++17
namespace A::B::C {void func1() {}
} //namespace A::B::C