2016-12-12 31 views
0

的考慮下面的輔助功能理由模板參數包擴展語法

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)...) - 但是這是無效的。看起來語言在不同的上下文中使用不同的邏輯。

不同語法背後的基本原理是什麼?

+2

無法使用參數包擴展,而無需使用[技巧](http://stackoverflow.com/鏈通話a/17340003/1794345) – Rerito

+0

@Rerito謝謝,但我知道這一招。不幸的是,這並沒有回答這個問題。 – plasmacel

回答

7

test3的情況下,我們將表達式test3(Is, args...)相對於Is「摺疊」。然而,我們必須編寫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);

調用這將是C++的17路:

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

這是不是一個真正的不同的語法。你有兩個你想要擴展的參數包:函數調用中的args和它周圍的Is,這意味着你有兩個...s。逗號只是表示這些是單獨的語句的一種方式。

...安置自由意味着你反正可以把它摺疊,你需要:

(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))。有時你需要前者,有時你需要後者。具有允許兩者的語法非常重要。

0

這是你的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)... };  
} 

參數包擴展可以在特定情境下的地方(通常是函數/模板參數/參數列表和括號,初始化列表)。像你所做的那樣,藍色的擴張是非法的。爲了規避這一點,我們需要在法律背景下進行,這裏是一個初始化列表。但是,我們必須確保不會形成不良的上述初始化列表:

  • 它不能爲空:test3_impl()返回void:所以我們在一開始
  • 它必須得到很好的輸入扔在一個0 ,所以我們使用逗號運算符:(<CALL>, void(), 0)void()在這裏是爲了防止逗號運算符重載,它被添加爲詳盡的,在您的示例中不是必需的。

最後,那個虛擬初始值列表必須存儲在某個地方,所以int數組是一個很好的佔位符。

但是,當你寫:

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

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