33

Có vẻ như đối số gói có thể được mở rộng chỉ thay cho thông số gói của mẫu bí danh. Đây không phải là đúng đối với một lớp hoặc một hàm template:Mở rộng gói cho mẫu bí danh

template <class T, class... Args> struct x { using type = T; }; 

template <class T, class... Args> using x_t  = typename x<T, Args...>::type; 
template <class... Args>   using x_fix_t = typename x<Args...>::type; 

template <class... Args> auto f(Args...) -> void { 
    typename x<Args...>::type v1; // OK 
    x_t<Args...> v2; // Error 
    x_fix_t<Args...> v3; // OK 
} 

đơn giản trường hợp:

template <class T, class U> using y_t = T; 

template <class... Args> auto f(Args...) -> void { 
    y_t<Args...> v4; // Error 
} 

Đoạn mã trên tạo ra lỗi (thậm chí nếu f không bao giờ được khởi tạo) với cả hai c++11c++14 trong g++ 4.9, g++ 5.1clang 3.5.

Tại sao điều này không được phép và quy tắc chung là gì? Tôi không thấy lý do nào để hạn chế điều này. Có vẻ như một lệnh cấm rất lạ.

Vì lý do tại sao không viết là x_fix_t với biến thể đầu tiên, rõ ràng hơn là x_t có đối số đầu tiên bắt buộc. (ví dụ đó là lý do f() không được phép). Nhưng điều này không quan trọng, việc sửa chữa rất dễ dàng. Câu hỏi còn lại: Tại sao?

gcc lỗi:

error: pack expansion argument for non-pack parameter ‘T’ of 
alias template ‘template<class T, class ... Args> using x_t = typename x::type’ 

kêu vang lỗi:

error: pack expansion used as argument for non-pack parameter of 
alias template x_t<Args...> v2; 
+2

Thông tin ở đây có liên quan, tôi nghĩ: http://stackoverflow.com/q/24433658/4326278 – bogdan

+0

Ngoài ra, đối với những gì nó có giá trị, MSVC 12 và 14 RC biên dịch điều này mà không có chẩn đoán (ngoài cảnh báo bình thường cho unreferenced biến) - "phương sai triển khai", như họ nói. – bogdan

+0

Hoạt động OK trong g ++ 4.8.2. –

Trả lời

14

này biên dịch trong GCC 4.8 nhưng bị lỗi trong GCC 4.9, đó là bằng chứng cho thấy nó có liên quan đến DR1430 và bug report #59498. Việc sửa chữa bởi Roy Chrihfield đề xuất là một trong những chính xác giống như bạn:

Rewriting the code to use a struct succeeds: 

template <typename T, typename ...> 
struct alias { using type = T; }; 

template <typename ...T> 
using variadic_alias = typename alias<T...>::type; 

Hơn nữa, Jason Merrill trau chuốt về lý do tại sao nó nên thất bại:

Actually, no, this is very much a Core 1430 issue; there's no way to mangle variadic_alias without mentioning the name of an alias template in the mangling, and they're supposed to be entirely transparent. This only works in 4.8 by accident because checking is disabled for the release.

Không thảo luận thêm tồn tại trong báo cáo lỗi, vì vậy chúng tôi có thể chuyển sang DR1430:

Originally, a pack expansion could not expand into a fixed-length template parameter list, but this was changed in N2555. This works fine for most templates, but causes issues with alias templates.

In most cases, an alias template is transparent; when it's used in a template we can just substitute in the dependent template arguments. But this doesn't work if the template-id uses a pack expansion for non-variadic parameters. For example:

template<class T, class U, class V> 
struct S {}; 

template<class T, class V> 
using A = S<T, int, V>; 

template<class... Ts> 
void foo(A<Ts...>); 

There is no way to express A in terms of S, so we need to hold onto the A until we have the Ts to substitute in, and therefore it needs to be handled in mangling.

Currently, EDG and Clang reject this testcase, complaining about too few template arguments for A. G++ did as well, but I thought that was a bug. However, on the ABI list John Spicer argued that it should be rejected.

(See also issue 1558.)

Notes from the October, 2012 meeting:

The consensus of CWG was that this usage should be prohibited, disallowing use of an alias template when a dependent argument can't simply be substituted directly into the type-id.

Additional note, April, 2013:

For another example, consider:

template<class... x> class list{}; 
    template<class a, class... b> using tail=list<b...>; 
    template <class...T> void f(tail<T...>); 

    int main() { 
    f<int,int>({}); 
    } 

There is implementation variance in the handling of this example.

Trong các từ khác, đây là sự cố đang diễn ra mà không có bất kỳ độ phân giải nào (AFAIC).

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