省略符號及可變參數範本

本文說明如何使用省略號 ( ... ) 搭配 C++ 變數範本。 省略號在 C 和 C++ 中有許多用途。 這些包含函式的變數引數清單。 來自 C 執行時間程式庫的函 printf() 式是其中一個最知名的範例。

variadic 範本 是支援任意數目引數的類別或函式範本。 此機制特別適用于 C++ 程式庫開發人員:您可以將它套用至類別範本和函式範本,進而提供廣泛的型別安全和非簡單功能和彈性。

語法

variadic 範本會以兩種方式使用省略號。 在參數名稱的左邊,它會表示 參數套件 ,並在參數名稱的右邊,將參數套件展開為個別的名稱。

以下是 variadic 類別範本 定義語法的基本範例

template<typename... Arguments> class classname;

針對參數套件和擴充,您可以根據您的喜好設定,在省略號周圍新增空白字元,如下列範例所示:

template<typename ...Arguments> class classname;

或此範例:

template<typename ... Arguments> class classname;

本文使用第一個範例中顯示的慣例(省略號附加至 typename )。

在上述範例中, Arguments 是參數套件。 類別 classname 可以接受可變數目的引數,如下列範例所示:

template<typename... Arguments> class vtclass;

vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;
vtclass<long, std::vector<int>, std::string> vtinstance4;

藉由使用 variadic 類別範本定義,您也可以至少需要一個參數:

template <typename First, typename... Rest> class classname;

以下是 variadic 函式範本 語法的基本範例

template <typename... Arguments> returntype functionname(Arguments... args);

然後,參數 Arguments 套件會展開以供使用,如下一節所示。

其他形式的 variadic 函式範本語法是可行的,包括但不限於下列範例:

template <typename... Arguments> returntype functionname(Arguments&... args);
template <typename... Arguments> returntype functionname(Arguments&&... args);
template <typename... Arguments> returntype functionname(Arguments*... args);

也允許像 這樣的 const 規範:

template <typename... Arguments> returntype functionname(const Arguments&... args);

如同 variadic 範本類別定義,您可以讓至少需要一個參數的函式:

template <typename First, typename... Rest> returntype functionname(const First& first, const Rest&... args);

Variadic 範本會使用 sizeof...() 運算子(與較舊的 sizeof() 運算子無關):

template<typename... Arguments>
void tfunc(const Arguments&... args)
{
    constexpr auto numargs{ sizeof...(Arguments) };

    X xobj[numargs]; // array of some previously defined type X

    helper_func(xobj, args...);
}

進一步了解省略符號位置

本文先前說明以這個形式定義參數套件和展開的省略號位置:「在參數名稱左邊,它會表示參數套件,並在參數名稱的右邊,將參數套件展開為個別名稱」。 雖然在技術上是真的,但在翻譯至程式碼時可能會造成混淆。 考量:

  • 在 template-parameter-list 中, template <parameter-list>typename... 引進範本參數套件。

  • 在 parameter-declaration-clause 中, func(parameter-list) 「最上層」省略號引進函式參數套件,省略號定位很重要:

    // v1 is NOT a function parameter pack:
    template <typename... Types> void func1(std::vector<Types...> v1);
    
    // v2 IS a function parameter pack:
    template <typename... Types> void func2(std::vector<Types>... v2);
    
  • 若省略符號在參數名稱之後顯示,您有參數封裝展開。

範例

說明 variadic 函式範本機制的好方法是在重寫 的一些功能 printf 時使用它:

#include <iostream>

using namespace std;

void print() {
    cout << endl;
}

template <typename T> void print(const T& t) {
    cout << t << endl;
}

template <typename First, typename... Rest> void print(const First& first, const Rest&... rest) {
    cout << first << ", ";
    print(rest...); // recursive call using pack expansion syntax
}

int main()
{
    print(); // calls first overload, outputting only a newline
    print(1); // calls second overload

    // these call the third overload, the variadic template,
    // which uses recursion as needed.
    print(10, 20);
    print(100, 200, 300);
    print("first", 2, "third", 3.14159);
}

輸出

1
10, 20
100, 200, 300
first, 2, third, 3.14159

注意

大部分納入 variadic 函式範本的實作都會使用某種形式的遞迴,但與傳統遞迴稍有不同。 傳統的遞迴牽涉到使用相同簽章來呼叫本身的函式。 (可能會多載或範本化,但每次都會選擇相同的簽章。Variadic 遞迴牽涉到使用不同(幾乎一律遞減)引數數目來呼叫 variadic 函式範本,藉此每次戳出不同的簽章。 仍然需要「基底案例」,但遞迴的性質不同。