變長引數
允許函式接受任意數量的額外引數。
如果函式的引數列表的最後一個引數是省略號(...),則該函式是變參函式。
省略號之前的逗號可以省略。 | (C++26 中已棄用) |
// the function declared as follows int printx(const char* fmt, ...); int printx(const char* fmt...); // same as above, but deprecated since C++26 // may be called with one or more arguments: printx("hello world"); printx("a=%d b=%d", a, b); int printy(..., const char* fmt); // error: ... can only be the last parameter int printz(...); // valid, but the arguments cannot be accessed portably
這與函式引數包展開不同,引數包展開由作為引數宣告符一部分的省略號指示,而不是單獨的省略號引數。引數包展開和“變參”省略號都可以出現在函式模板的宣告中,例如std::is_function。 |
(C++11 起) |
目錄 |
[編輯] 預設引數提升
當呼叫變參函式時,在左值到右值、陣列到指標以及函式到指標的轉換之後,作為可變引數列表一部分的每個引數都會進行額外的轉換,稱為預設引數提升
|
(C++11 起) |
非 POD 類型別(C++11 前)作用域列舉和具有合格的非平凡複製建構函式、合格的非平凡移動建構函式或非平凡解構函式的類型別(C++11 起) 在潛在求值呼叫中是條件支援的,其語義由實現定義(這些型別在未求值呼叫中始終受支援)。
由於變參引數在過載決議中具有最低的等級,它們通常在 SFINAE 中用作包羅永珍的備用方案。
在使用變參引數的函式體內,可以使用 <cstdarg>
庫工具訪問這些引數的值
定義於標頭檔案
<cstdarg> | |
啟用對變長函式引數的訪問 (函式宏) | |
訪問下一個變長函式引數 (函式宏) | |
(C++11) |
複製變長函式引數 (函式宏) |
結束變長函式引數的遍歷 (函式宏) | |
儲存 va_start、va_arg、va_end 和 va_copy 所需的資訊 (型別定義) |
如果省略號之前的最後一個引數具有引用型別,或者其型別與預設引數提升後的型別不相容,則 va_start 宏的行為是未定義的。
(C++11 起) |
[編輯] 替代方案
|
(C++11 起) |
[編輯] 注意
在 C 程式語言中,直到 C23,至少一個命名引數必須出現在省略號引數之前,因此 R printz(...); 在 C23 之前無效。在 C++ 中,即使無法訪問傳遞給此類函式的引數,也允許這種形式,並且通常在 SFINAE 中用作備用過載,利用省略號轉換在過載決議中的最低優先順序。
這種變參語法於 1983 年引入 C++,省略號前沒有逗號。當 C89 從 C++ 採用函式原型時,它用一個需要逗號的語法替換了它。為了相容性,C++98 同時接受 C++ 風格的 f(int n...) 和 C 風格的 f(int n, ...)。原始的 C++ 風格語法自 C++26 起已棄用。
逗號可以在簡寫函式模板中使用,以使省略號表示變參函式而不是變參模板 void f1(auto...); // 等同於 template<class... Ts> void f3(Ts...) |
(C++20 起) |
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 506 | C++98 | 將非 POD 類引數傳遞給 省略號會導致未定義行為 |
傳遞此類引數是 條件支援的,且語義由 實現定義 |
CWG 634 | C++98 | 條件支援的類型別 導致某些 SFINAE 慣用法失效 |
如果未求值,則始終支援 |
CWG 2247 | C++11 | 沒有限制將引數包 或 lambda 捕獲傳遞給 va_start |
導致格式錯誤, 無需診斷 |
CWG 2347 | C++11 | 不清楚傳遞給省略號的 作用域列舉是否受預設引數提升影響 |
傳遞作用域列舉 是條件支援的,且語義由 實現定義 |
[編輯] 參閱
C 文件 關於 變參(Variadic arguments)
| |
C 文件 關於 隱式轉換(Implicit conversions)
|