簡體   English   中英

模板參數包擴展語法的原理

[英]Rationale of template parameter pack expansion syntax

鑒於以下輔助功能

template <typename ... Ts>
auto f(Ts&& ... args) {}

template <typename T>
auto g(T x) { return x; }

1)我們像往常一樣擴展模板參數包。

template <typename ... Ts>
void test1(Ts&& ... args)
{
    f(args...);
}

2)在這里,擴展...發生在g()函數調用之后。 這也是合理的,因為g()是與每個args調用的:

template <typename ... Ts>
void test2(Ts&& ... args)
{
    f(g(args)...);
}

3)用相同的邏輯,我期望test3(Is, args...)... ,但是沒有。 您必須編寫test3(Is..., args...)

template <typename ... Ts>
void test3(size_t i, Ts&& ... args)
{
    f(args...);
}

template <typename ... Ts>
void test3(std::index_sequence<Is...>, Ts&& ... args)
{
    // would expect test3(Is, args...)...;
    test3(Is..., args...);
}

我知道,我用,但是,我不明白。 模板擴展的整個概念是表達折疊的一種形式。 不是以C ++ 17的方式,而是以...之前的子表達式相對於可變參數折疊(或根據需要重復)的意義。 test3情況下,我們相對於Is “折疊”表達式test3(Is, args...) 但是我們必須編寫test3(Is..., args...)而不是test3(Is, args...)...

有了這種標准的怪異邏輯,您也可以寫f(g(args...))代替f(g(args)...) -但這是無效的。 看起來該語言在不同的上下文中使用了不同的邏輯。

不同語法背后的原理是什么?

test3情況下,我們相對於Is “折疊”表達式test3(Is, args...) 但是我們必須編寫test3(Is..., args...)而不是test3(Is, args...)....

這實際上是不正確的。 test3(Is..., args...)將展開Is到位,然后args到位。 因此,調用test3(index_sequence<0,1,2>, x, y, z)最終將調用test3(0, 1, 2, x, y, z) ,這不是您想要發生的事情。 您需要test3(0, x, y, z); test3(1, x, y, z); test3(2, x, y, z); test3(0, x, y, z); test3(1, x, y, z); test3(2, x, y, z);

C ++ 17調用此方法的方式是:

(test3(Is, args...), ...);

這實際上不是一個不同的語法。 你必須要拓展不同的雙參數包: args函數調用中和Is它周圍,這樣就意味着你有兩個...秒。 逗號只是表明它們是單獨的語句的一種方式。

自由放置...意味着您可以根據需要折疊它:

(test3(Is, args), ...);    // test3(0,x); test3(1,y); test3(2,z);
(test3(Is..., args), ...); // test3(0,1,2,x); test3(0,1,2,y); test3(0,1,2,z);
test3(Is..., args...);     // test3(0,1,2,x,y,z);

有了這種標准的怪異邏輯,您也可以寫f(g(args...))代替f(g(args)...) -但這是無效的

那不是奇怪的邏輯。 那意味着不同的事情。 第一個擴展為f(g(a0, a1, a2, ..., aN)) ,第二個擴展為f(g(a0), g(a1), g(a2), ..., g(aN)) 有時您需要前者,有時則需要后者。 具有同時允許兩者的語法非常重要。

這是您的test3樣子:

template <typename ... Ts>
void test3_impl(size_t i, Ts&&... args) {
    f(std::forward<Ts>(args)...);
}

template <size_t ... Is, typename ... Ts>
void test3(std::index_sequence<Is...>, Ts&&... args) {
    int dummy[] = { 0, (test3_impl(Is, args...), void(), 0)... };      
}

參數包擴展可以在特定的上下文中進行(通常是函數/模板參數/參數列表和大括號初始化列表)。 像您一樣突然進行擴展是非法的。 為了避免這種情況,我們需要使其在合法的上下文中發生,這里是一個初始化列表。 但是,我們必須確保所述初始化列表沒有格式錯誤:

  • 它不能為空:因此我們在開始時輸入0
  • 必須輸入test3_impl()的類型: test3_impl()返回void,因此我們使用逗號運算符: (<CALL>, void(), 0) void()此處用於防止逗號運算符重載,它被添加為詳盡無遺,在您的示例中不是必需的。

最后,該虛擬初始化程序列表必須存儲在某個位置,因此int數組是一個很好的占位符。

但是,當您編寫時:

// Assuming Is... is [I1, IsTail...]
test3_impl(Is..., args...);

這實際上調用f(IsTail..., args...)而不是f(args...)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM