5

Chỉnh sửa: Tôi đã mắc lỗi đơn giản khi sử dụng SFINAE. Sửa lỗi đó giải quyết lỗi trình biên dịch mà tôi đề cập bên dưới. Tuy nhiên tôi vẫn tò mò về lý do tại sao thông số mẫu không thể được suy ra trong trường hợp này.Tại sao thông số mẫu này không được khấu trừ?

Tôi muốn viết một bản mẫu metaprogram mẫu C++ 14 để tính ước số chung lớn nhất (GCD) của số std::integer_sequence. Sau khi một số mày mò tôi đến với gần ví dụ hoàn chỉnh này:

template <typename T, T A, T B, T... Ints> 
struct GCD<std::integer_sequence<T, A, B, Ints...>> : 
     GCD<typename std::integer_sequence<T, GCD_pair<T, A, B>::value, Ints...>> {}; 

template <class T, T A, T B> 
struct GCD<std::integer_sequence<T, A, B>> : 
     GCD_pair<T, A, B> {}; 

int main() {  
    using seq = std::integer_sequence<int, 65537, 5, 10>; 
    cout << GCD<seq>::value << endl; 
    return 0; 
} 

Tôi chỉ bóc hai yếu tố đầu tiên của dãy số nguyên, và tìm ra GCD trong số họ sử dụng để được viết GCD_pair metafunction. Sau đó, tôi áp dụng GCD cho kết quả của GCD_pair và các yếu tố còn lại.

Các "rõ ràng" thi hành GCD_pair không biên dịch:

// This does not work: 
// type 'T' of template argument '0' depends on a template parameter 
template <typename T, T M, T N> 
struct GCD_pair : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

template <typename T, T M> 
struct GCD_pair<T, M, 0> : std::integral_constant<T, M> {}; 

Vì vậy, tôi đã cố gắng thực hiện khác có thể sử dụng SFINAE:

// This doesn't work either: 
// template parameters not deducible in partial specialization 
template <typename T, T M, T N, typename = void> 
struct GCD_pair : std::integral_constant<T, M> {}; 

template <typename T, T M, T N, typename std::enable_if<(M % N != 0)>::type> 
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Edit: Tôi đã thực hiện một lỗi. Xem câu trả lời dưới đây để sửa chữa việc sử dụng SFINAE của tôi. Nhưng tôi vẫn tò mò về câu hỏi của tôi:

Tại sao thông số mẫu typename std::enable_if<(M % N != 0)>::type không được khấu trừ? Nó không được khấu trừ bao giờ về nguyên tắc, hoặc có thể tham số như trong trường hợp này thực sự được suy luận trong thực tế? Nói cách khác, điều này có thể được coi là một giám sát thực hiện trình biên dịch?

Đối với những gì đáng giá, tôi có thể triển khai a slightly different version bằng cách "ẩn" điều kiện (M % N != 0) có điều kiện trong thông số mẫu bool. Tuy nhiên, Tôi nghĩ cả hai điều trên là triển khai hợp lýoperator% và so sánh với 0 với operator!= được định nghĩa hoàn hảo cho tất cả các loại tích phân của C++.

Trả lời

5

Nó nên là:

template <typename T, T M, T N> 
struct GCD_pair<T, M, N, typename std::enable_if<(M % N != 0)>::type> 
    : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Bởi vì bạn đang sử dụng C++ 14, bạn cũng có thể đơn giản hóa nó bằng cách sử dụng std::enable_if_t:

template <typename T, T M, T N> 
struct GCD_pair<T, M, N, std::enable_if_t<(M % N != 0)>> 
    : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Trong cả hai trường hợp, nếu điều kiện (M % N != 0) áp dụng, cả mẫu chính và chuyên môn đều hợp lệ khi đã được khởi tạo, nhưng chuyên môn hóa, tốt hơn, chuyên biệt hơn và do đó nó được chọn.
Ở phía bên kia, nếu điều kiện không áp dụng, chuyên môn sẽ bị loại bỏ âm thầm vì các quy tắc sfinae, nhưng mẫu chính vẫn hợp lệ và do đó được chọn.

Cũng lưu ý rằng type trong typename std::enable_if<(M % N != 0)>::typevoid khi điều kiện là đúng.
Do đó, bạn template danh sách các thông số sẽ là về mặt lý thuyết:

template <typename T, T M, T N, void> 
struct GCD_pair<T, M, N, void>: std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Tuy nhiên, void không được phép là một tổ chức phi kiểu mẫu tham số.

Cuối cùng, từ tiêu chuẩn chúng tôi có mà:

Một đặc tả từng phần phù hợp với một danh sách đối số mẫu thực tế đưa ra nếu các đối số mẫu của chuyên môn hóa một phần có thể được suy ra từ các mẫu thực tế danh sách đối số

Đồng thời:

Nếu các đối số mẫu của một chuyên môn từng phần không thể suy luận được do cấu trúc templa của nó te-parameter-list và template-id, chương trình không đúng định dạng.

Trong trường hợp của bạn, tham số thứ tư của chuyên môn không thể được suy luận vì lý do rõ ràng. Ngay cả khi nó là hợp lệ (và nó không phải là, bởi vì nó kết quả trong void như đã đề cập), những gì bạn nhận được là một loại hoặc một tham số không gõ mà bạn không có một loại thực tế hoặc giá trị.
Giả sử bạn có chuyên môn sau:

template <typename T, T M, T N, int> 
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Làm thế nào trình biên dịch có thể suy luận ra giá trị cho các tham số mẫu cuối cùng?
Nó không thể và đó là nhiều hơn hoặc ít hơn trường hợp của bạn. Có lẽ nó thay vì phải phát hiện thực tế là danh sách tham số bị hỏng nếu điều kiện để std::enable_if hợp lệ, dù sao cả hai đều là lỗi và bạn đang nhận được một trong số chúng trong giai đoạn biên dịch.

Những gì bạn sẽ được hưởng lợi từ là một cái gì đó như thế này:

template <typename T, T M, T N, typename = std::enable_if_t<(M % N != 0)>> 
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Hoặc này:

template <typename T, T M, T N, std::enable_if_t<(M % N != 0)>* = nullptr> 
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Dù sao, cả hai đều không hợp lệ cho các đối số mặc định không được phép vào chuyên ngành một phần.

+0

Ngoài ra, 'GCD' cần mẫu chính, ví dụ: 'mẫu struct GCD;'. – Corristo

+0

Chắc chắn rồi! Silly tôi, tôi đã thực hiện một sai lầm đơn giản. – JohnDuck

+0

Vì vậy, tôi nhanh chóng chấp nhận điều này bởi vì bạn đã chỉ ra lỗi của tôi khi sử dụng SFINAE - tuy nhiên tôi đã xem xét lại vì câu hỏi của tôi vẫn đứng vững. Tại sao thông số mẫu không được suy luận? – JohnDuck

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