C++模板元 -- 變長參數模板

基礎

變長參數模板的一般格式是:

// 編譯遞歸的終止位置
template <T arg>
void foo(T arg) {
	// do something with arg
}

// 展開編譯表達式
template <typename T, typename ...Args>
void foo(T arg, Args... args) {
    foo(arg, args...);
}

上述的代碼中,需要有一個特化的函數,轉麼作爲編譯器遞歸終止的出口。在C++17以及以上的版本中,可以使用constexpr結合sizeof...關鍵字,實現模板編譯器的遞歸。

給出代碼示例,注意都是編譯器展開的代碼模板,而非運行期。

#include <iostream>
#include <string>

template <typename T, typename ...Args>
void Print(T&& arg, Args&&... args) {
    std::cout << arg << std::endl;
    if constexpr (sizeof...(args) > 0) {
        std::cout << "remaining args = " << sizeof...(args) << std::endl;
        Print(args...);
    }
}

int main() {
    Print(1, std::string("hello"), 3.14, 'c');
    return 0;
}

上述代碼中,sizeof...計算的是剩餘參數的數量,而if constexpr表示的編譯器的條件判斷

拓展

變長模板是可以執行編譯期計算的,比如爲參數添加上對應的值,代碼示例:

#include <iostream>
#include <string>

template<typename T, typename ...Args>
void Print(T &&arg, Args &&... args) {
    std::cout << arg << std::endl;
    if constexpr (sizeof...(args) > 0) {
        Print(args...);
    }
}

template<typename... Args>
void PrintDouble(Args &&... args) {
    Print(args + args...); // 對應位置的參數相加
}

template<typename... Args>
void PrintAdd1(Args &&... args) {
    Print(args + 1 ...);  // 每個參數加1,不適合string
}

int main() {
    PrintDouble(1, std::string("hello"), 3.14);
    PrintAdd1(1, 3.14);
    return 0;
}

對於索引來說,同樣適用,給個代碼示例

#include <iostream>
#include <vector>

template<typename T, typename... Args>
void Print(T &&arg, Args &&... args) {
    std::cout << arg << std::endl;
    if constexpr (sizeof...(args) > 0) {
        Print(args...);
    }
}

template<size_t... Idx, typename Container>
void PrintIdx(Container &&c) {
    Print(c[Idx]...);
}

int main() {
    std::vector<int> vec{0, 1, 2, 3, 4, 5};
    PrintIdx<0, 4, 2 ,3, 1>(vec);
    return 0;
}

參考

  • https://zh.cppreference.com/w/cpp/language/fold
  • https://cloud.tencent.com/developer/article/1347876
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章