C++中constexpr 與 explicit關(guān)鍵字使用實戰(zhàn)樣例
概述
constexpr:聲明“該實體可以在編譯期求值”,隨著 C++ 標準被逐步放寬,現(xiàn)在能在更多場景寫編譯期函數(shù)/構(gòu)造器/變量。C++20 還新增了consteval(強制編譯期)與constinit(強制常量初始化)。explicit:用于阻止不期望的隱式轉(zhuǎn)換。除了對構(gòu)造函數(shù)適用外,C++11 開始可標注轉(zhuǎn)換運算符(conversion operators);C++20 引入了條件explicit(expr)能按表達式?jīng)Q定是否為顯式。
一、constexpr深度講解
1. 含義(核心語義)
constexpr 表示:函數(shù)/構(gòu)造器/變量在滿足條件時可以在編譯期求值,從而能用作常量表達式(用于數(shù)組維度、模板非類型參數(shù)、static_assert 等)。
- 對象(變量)使用
constexpr:其初始化器必須是常量表達式(直到 C++14 的限制放寬后更靈活)。 - 函數(shù)/構(gòu)造函數(shù)使用
constexpr:表明在滿足參數(shù)均為常量表達式的情況下,函數(shù)可被用于編譯期求值;否則仍可在運行時調(diào)用。
2. 演進變化
constexpr 的能力隨標準放寬:
- C++11:首次引入,函數(shù)體必須是單一
return(很受限)。 - C++14:允許更復(fù)雜的函數(shù)體(局部變量、循環(huán)、分支),
constexpr函數(shù)功能大增。 - C++17:語義繼續(xù)放寬,數(shù)組/結(jié)構(gòu)體初始化等更多場合支持常量求值。
- C++20:引入
consteval(強制在編譯期求值)和constinit(強制靜態(tài)變量為常量初始化),并繼續(xù)擴展constexpr支持(例如允許虛函數(shù)在某些情形下為constexpr)。
新關(guān)鍵詞對比(C++20)
constexpr:可以在編譯期或運行期求值(視調(diào)用上下文而定)。consteval:必須在編譯期求值,若在運行期調(diào)用則編譯錯誤。用來寫“即時常量函數(shù)”。constinit:用于靜態(tài)/線程局部變量,強制其進行常量初始化(防止靜態(tài)初始化次序問題)。
3.constexpr的常見用法示例
3.1constexpr變量
constexpr int square(int x) { return x * x; } // C++14 起允許復(fù)雜體
constexpr int five = 5;
constexpr int twentyfive = square(five); // 編譯期求值
static_assert(twentyfive == 25);
3.2constexpr函數(shù)(運行期與編譯期兩用)
constexpr int fib(int n) {
if (n <= 1) return n;
return fib(n-1) + fib(n-2);
}
int main() {
constexpr int f5 = fib(5); // 編譯期
int k; cin >> k;
int r = fib(k); // 運行期也可調(diào)用
}
3.3constexpr構(gòu)造函數(shù)(常用于字面類型)
struct Point {
double x, y;
constexpr Point(double a, double b) : x(a), y(b) {}
constexpr double norm2() const { return x*x + y*y; }
};
constexpr Point p{3.0, 4.0};
static_assert(p.norm2() == 25.0);注意
- 直到 C++20,
constexpr成員函數(shù)和構(gòu)造器的語義被逐步增強(允許更多操作,如改變constexpr成員變量等)。
4.consteval與constinit
consteval int must_be_constexpr(int n) { return n*2; } // 必須在編譯期調(diào)用
constinit int g = must_be_constexpr(10); // g 在編譯期初始化
consteval 用于那些希望強制在編譯期完成的計算(例如生成編譯期表或做元編程檢查)。
5. 常見誤區(qū)與陷阱(constexpr)
- 不是所有
constexpr函數(shù)調(diào)用都會在編譯期執(zhí)行:是否在編譯期執(zhí)行取決于調(diào)用時傳入的參數(shù)是否為常量表達式以及使用場景(需要編譯期值時才強制求值)。 constexpr函數(shù)內(nèi)部不能使用非constexpr的操作(例如動態(tài)分配、IO),否則在嘗試編譯期求值時會失?。ǖ摵瘮?shù)仍可在運行期被調(diào)用,除非用consteval)。- 不要把
constexpr當作“強制內(nèi)聯(lián)或優(yōu)化提示” —— 它的本意是可編譯期求值;優(yōu)化與否由編譯器決定。 constexpr與noexcept互不替代,但常見constexpr函數(shù)也聲明noexcept(如果它們無異常)以改善使用場景。
6. 實戰(zhàn)建議(constexpr)
- 對純計算、常量生成函數(shù)使用
constexpr:可在編譯期做更多檢查并得到更高性能。 - 若函數(shù)必須在編譯期求值(設(shè)計要求),使用
consteval。 - 對類提供
constexpr構(gòu)造函數(shù) / 方法以便可在編譯期創(chuàng)建字面量對象(例如元數(shù)據(jù)、表格)。 - 在大型項目中謹慎使用
constexpr以避免過度編譯期計算導(dǎo)致編譯時間增加。
有關(guān) constexpr 的標準細節(jié)參考:cppreference 的 constexpr 條目。
二、explicit深度講解
1. 含義(核心語義)
explicit 的目的是 禁止編譯器執(zhí)行某些隱式轉(zhuǎn)換,從而避免意外、難以察覺的類型轉(zhuǎn)換錯誤。它可以修飾:
- 構(gòu)造函數(shù)(阻止從單參數(shù)構(gòu)造函數(shù)的隱式轉(zhuǎn)換) — 自 C++98。
- 用戶定義的轉(zhuǎn)換運算符(conversion operator)— 自 C++11。
- C++20 引入:
explicit(expression),可以根據(jù)編譯期表達式條件性地使其顯式。
2. 為什么需要explicit
隱式轉(zhuǎn)換在方便的同時會引發(fā)難以發(fā)現(xiàn)的邏輯錯誤、二義性或意外重載匹配,explicit 能把“自動發(fā)生”的轉(zhuǎn)換變成“必須寫成 T(x) 或 static_cast<T>(x) 的顯式轉(zhuǎn)換”,提高代碼可讀性與安全性。
3.explicit用法示例
3.1 對構(gòu)造函數(shù)
struct A {
explicit A(int x) : v(x) {}
int v;
};
void foo(A a) {}
foo(10); // 錯誤:A(int) 為 explicit,禁止隱式轉(zhuǎn)換
foo(A(10)); // 正確(顯式)
foo(static_cast<A>(10)); // 正確3.2 對轉(zhuǎn)換運算符(conversion operator)
C++11 起可以寫 explicit operator T() const,從而禁止隱式轉(zhuǎn)換為 T:
struct S {
explicit operator bool() const { return true; }
};
S s;
if (s) { } // 不能:implicit conversion to bool is not allowed?
// Actually `if (s)` requires context of boolean; for explicit operator bool, direct-initialization in if condition uses explicit? Explanation below.說明:
explicit operator bool()的引入是為了替代“safe bool idiom”。explicit轉(zhuǎn)換運算符不會參與某些隱式轉(zhuǎn)換場景,從而避免意外使用。標準對什么時候允許使用顯式轉(zhuǎn)換運算符(例如在if (expr)、static_cast<bool>(expr)、直接初始化等)做了具體規(guī)定。詳見cppreference。
3.3 C++20 條件explicit(expr)
可以根據(jù)模板參數(shù)或常量條件使構(gòu)造函數(shù)/轉(zhuǎn)換運算符有條件地顯式:
template<typename T>
struct Wrapper {
explicit(sizeof(T) > 4) Wrapper(T); // C++20: 如果 T 大于4字節(jié),則構(gòu)造器為 explicit
};
或?qū)D(zhuǎn)換運算符:
struct X {
explicit(sizeof(int) <= 4) operator int() const;
};
這種寫法讓模板庫可以更精細控制隱式轉(zhuǎn)換行為。
4. 顯式轉(zhuǎn)換運算符的語義細節(jié)
- 何時可以用顯式轉(zhuǎn)換運算符用于上下文? 標準允許在某些上下文需要顯式轉(zhuǎn)換的場合使用
explicit運算符(例如帶有直接初始化的場合、顯式類型轉(zhuǎn)換static_cast、以及某些語句/條件表達式),而在需要隱式轉(zhuǎn)換的上下文中(如拷貝初始化)是不被自動使用的。cppreference對這些規(guī)則有詳細列出。 explicit operator bool()與if (obj):if語句要求 “contextually convertible to bool” —— 這允許顯式的operator bool()被用于條件判斷(即if (obj)會觸發(fā)顯式轉(zhuǎn)換運算符)——這就是為什么explicit operator bool()成為 “safe bool” 的現(xiàn)代替代。更多細節(jié)見實踐資源。
5. 常見誤用與陷阱(explicit)
- 把每個構(gòu)造函數(shù)都寫成
explicit會減少便利性 —— 設(shè)計上應(yīng)僅對單參數(shù)(或可被單參數(shù)調(diào)用的)構(gòu)造函數(shù)考慮explicit,以防意外隱式轉(zhuǎn)換。 - 對于 conversion operator,默認不要輕易設(shè)為 implicit(非
explicit),除非非常確定該類型需要自然地作為目標類型使用(例如std::string::operator std::string_view()的情況)。一般建議:prefers named conversion function(如.to_string())而不是提供很多隱式 conversion operators。
6. 推薦實踐(explicit)
- 對單參數(shù)構(gòu)造函數(shù)通常使用
explicit,除非希望類型在表達式中自動構(gòu)造(例如某些數(shù)值/枚舉封裝,需權(quán)衡)。 - 對用戶自定義的轉(zhuǎn)換運算符,優(yōu)先考慮
explicit,只在確實需要隱式轉(zhuǎn)換時才允許隱式。 - 使用 C++20 的
explicit(expr)在模板庫中做細粒度控制,而不是寫很多重載/模板特化。
三、constexpr與explicit的交互要點
- 可以寫
constexpr explicit構(gòu)造函數(shù)(非常常見):
struct S {
constexpr explicit S(int x): v(x) {}
int v;
};
constexpr S s = S(3); // ok: explicit but direct-initialization- 也可以寫
constexpr explicit operator T()(C++11 起),該轉(zhuǎn)化可在編譯期用于常量表達式求值(只要上下文允許)。
四、實戰(zhàn)樣例(綜合示例)
#include <type_traits>
// C++20 風(fēng)格:條件 explicit + constexpr + consteval 示范
struct Big {
int x;
constexpr explicit Big(int v) : x(v) {} // constexpr + explicit 構(gòu)造器
explicit operator int() const { return x; } // explicit conversion operator
};
consteval int must_be_ctime() { return 42; } // 強制編譯期求值 (C++20)
constinit int g = must_be_ctime(); // 強制常量初始化 (C++20)
static_assert(std::is_same_v<decltype(static_cast<int>(Big(3))), int>); // OK
// auto a = Big(3); // if function takes Big by value, implicit construction not allowed五、常見問題快速問答
Q:constexpr 函數(shù)內(nèi)部能用循環(huán)/分支嗎?
A:可以(從 C++14 起放寬),但用于編譯期時函數(shù)體中的所有操作必須可在編譯期執(zhí)行(不能進行未允許的運行時操作)。
Q:consteval 和 constexpr 的區(qū)別?
A:consteval 強制編譯期求值;constexpr 允許編譯期也允許運行期調(diào)用。
Q:explicit operator 什么時候應(yīng)該使用?
A:當你想阻止類型被無意間隱式轉(zhuǎn)換(尤其是布爾/數(shù)值上下文)時應(yīng)使用 explicit;否則可能引發(fā)模糊或錯誤的重載選擇。
六、參考資料
constexpr specifier (since C++11)— cppreference(詳述constexpr演進與限制)。Conversion function (operator type) — cppreference(說明explicit operator的語法與語義,及 C++20 條件 explicit)。const vs constexpr vs consteval vs constinit(總結(jié)與對比,含 C++20 新關(guān)鍵字)。
到此這篇關(guān)于C++中constexpr 與 explicit關(guān)鍵字使用實戰(zhàn)樣例的文章就介紹到這了,更多相關(guān)C++ constexpr 與 explicit使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
記逆向小白的第一次vbsedit 9爆破及內(nèi)存補丁制作過程
這篇文章主要介紹了記逆向小白的第一次vbsedit 9爆破及內(nèi)存補丁制作過程,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04
C++ std::make_unique和std::make_shared用法小結(jié)
本文主要介紹了C++ std::make_unique和std::make_shared用法,使用std::make_unique和std::make_shared能夠簡化動態(tài)分配內(nèi)存和構(gòu)造對象的過程,提高代碼的安全性和可讀性,感興趣的可以了解一下2023-11-11
c++中拷貝構(gòu)造函數(shù)的參數(shù)類型必須是引用
如果拷貝構(gòu)造函數(shù)中的參數(shù)不是一個引用,即形如CClass(const CClass c_class),那么就相當于采用了傳值的方式(pass-by-value),而傳值的方式會調(diào)用該類的拷貝構(gòu)造函數(shù),從而造成無窮遞歸地調(diào)用拷貝構(gòu)造函數(shù)。因此拷貝構(gòu)造函數(shù)的參數(shù)必須是一個引用2013-07-07

