2013-08-22 45 views
10

我想問下面的代碼是否有效。多個參數包在同一表達式中的多次擴展

我想知道在一個表達式中多次擴展參數包的可能性。

#include <iostream> 
#include <tuple> 

class ExpandWithConstructor 
{ 
    public: 
     template <typename ... T> 
     ExpandWithConstructor(T... args) { } 
}; 

template <typename T> 
int PrintArgs(T arg) 
{ 
    std::cout << arg << ", "; 
    return 0; 
} 

template <typename Head, typename ... T> 
class DebugPrinter: public DebugPrinter<T...> 
{ 
    public: 
     DebugPrinter() { } 

     template< typename ...Y> 
     DebugPrinter(Y ... rest) 
     { 
      std::cout << "Construction of: " << __PRETTY_FUNCTION__ << " Values: " ; 
      ExpandWithConstructor{PrintArgs(rest)...}; 
      std::cout << std::endl; 
     } 

}; 

template <typename Head> 
class DebugPrinter<Head> 
{ 
    public: 
}; 

template <typename ... T> 
class TypeContainer: public std::tuple<T...> 
{ 
    public: 
     TypeContainer(T... args):std::tuple<T...>(args...){}; 
}; 

template <typename... T1> class CheckVariadic; 

template <typename... T1, typename ...T2> 
class CheckVariadic< TypeContainer<T1...>, TypeContainer<T2...>> : 
      public DebugPrinter< T1, T2, T1...>... 
{ 
    public: 
     CheckVariadic(T1... args1, T2... args2, T1... args3): DebugPrinter< T1, T2, T1...>(args1, args2..., args1)... {} 
}; 


int main() 
{ 
    CheckVariadic< TypeContainer<int,float>, TypeContainer<char, void*>> checkVariadic1{ 1,2.2,'c',(void*)0xddddd,5,6.6,}; 
} 

正如你所看到的代碼使用: DebugPrinter < T1,T2,T1 ...> ...

如果T1與 「整型,浮點」 給予和T2是「字符,無效*」 它擴展到

DebugPrinter< T1, T2, int, float>... 

它擴展到

DebugPrinter< int, char, int, float> 
DebugPrinter< float, void*, int, float> 

同樣的擴張去與:

DebugPrinter< T1, T2, T1...>(args1, args2..., args1)... 

代碼與clang3.3編譯但不與gcc4.8.1所以我要問,如果代碼是有效還是無效。

更新: gcc 7.2仍然沒有編譯代碼。

+1

我可以通過使用'DebugPrinter使其以g ++ 4.7編譯 {args1,args2 ...,args1} ......「,但輸出與叮噹的方向相反。 – kennytm

+2

構造函數初始值設定項列表中函數調用的相反順序是g ++中已知的錯誤。我也可以用g ++ 4.7.2編譯代碼。但是g ++ 4.8.1仍然失敗。 – Klaus

+1

@KennyTM:準確地說,對於常規函數,參數的評估順序是未指定的;歷史上g ++已經使用從右到左的順序。對於一個初始化列表,我相信該命令是指定的,g ++根本沒有得到更新。 –

回答

3

是的,你的代碼是完全有效的。一個包擴展包含一個模式和一個省略號,並且可以出現在另一個包擴展的模式中。在該標準的第14.5.3/5段中,您將找到:

[...]參數包名稱的外觀只通過最內層封裝包擴展進行擴展。包擴展的模式應命名一個或多個未通過嵌套包擴展擴展的參數包; [...]

一個包擴展可以在§14.5.3/ 4中提到的任何上下文中使用。以您的示例爲例:

DebugPrinter< T1, T2, T1...>... 

兩個包擴展都是有效的。第一個的背景是template-argument-list,而第二個背景的出現在base-specifier-list

由標準文本提供的示例:

template<class ... Args> 
void g(Args ... args) {     // OK: Args is expanded by the function 
              // parameter pack args 
    f(const_cast<const Args*>(&args)...); // OK: 「Args」 and 「args」 are expanded 
    f(5 ...);        // error: pattern does not contain any 
              // parameter packs 
    f(args);        // error: parameter pack 「args」 is not 
              // expanded 
    f(h(args ...) + args ...);   // OK: first 「args」 expanded within h, 
              // second 「args」 expanded within f 
} 
+3

'f(args)...;'? – gnzlbg