2013-08-22 26 views
10

Tôi muốn hỏi xem mã sau có hợp lệ không.Nhiều mở rộng của nhiều gói thông số trong cùng một biểu thức

Tôi tự hỏi về khả năng mở rộng gói tham số nhiều lần trong một biểu thức.

#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,}; 
} 

Như bạn có thể thấy mã sử dụng: DebugPrinter < T1, T2, T1 ...> ...

nếu T1 được đưa ra với "int, float" và T2 là "char, void *" mà mở rộng để

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

mà mở rộng để

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

Việc mở rộng cùng đi với:

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

mã biên dịch với clang3.3 nhưng KHÔNG với gcc4.8.1 vì vậy tôi muốn hỏi nếu mã là hợp lệ hay không.

Cập nhật: gcc 7.2 vẫn chưa biên dịch mã.

+1

tôi có thể làm cho nó biên dịch trong g ++ 4.7 bằng cách sử dụng 'DebugPrinter {args1, args2 ..., args1} ... ', nhưng đầu ra ở hướng ngược lại của tiếng kêu. – kennytm

+2

Thứ tự đảo ngược của các cuộc gọi hàm từ một danh sách khởi tạo hàm dựng là một lỗi đã biết trong g ++. Tôi cũng có thể biên dịch mã bằng g ++ 4.7.2. Nhưng g ++ 4.8.1 vẫn không thành công. – Klaus

+1

@KennyTM: chính xác, đối với các hàm thông thường, thứ tự đánh giá của các đối số không được chỉ định; lịch sử g ++ đã sử dụng thứ tự từ phải sang trái. Đối với một danh sách initializer tôi tin rằng thứ tự được chỉ định và g + + chỉ đơn giản là không nhận được để cập nhật nó được nêu ra. –

Trả lời

3

Có mã của bạn là hoàn toàn hợp lệ. Một gói mở rộng bao gồm một mô hình và một dấu ba chấm và có thể xuất hiện trong một mô hình của một gói mở rộng. Trong đoạn §14.5.3/5 của tiêu chuẩn bạn sẽ tìm thấy:

[...] Một sự xuất hiện tên của một gói tham số chỉ được mở rộng bằng cách mở rộng gói trong cùng. Mẫu mở rộng gói sẽ đặt tên cho một hoặc nhiều gói tham số không được mở rộng bằng gói mở rộng lồng nhau; [...]

Mở rộng gói có thể được sử dụng trong bất kỳ ngữ cảnh nào được đề cập trong §14.5.3/4. Đưa ra ví dụ của bạn:

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

Cả hai mở rộng gói đều hợp lệ. Ngữ cảnh đầu tiên là template-argument-list trong khi cảnh thứ hai xuất hiện trong một số base-specifier-list.

Ví dụ được cung cấp bởi các văn bản chuẩn:

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

còn 'f (args) ...;' thì sao? – gnzlbg

Các vấn đề liên quan