clang / gcc không nhất quán trong chuyên ngành


9

Tôi đã gặp vấn đề này trong khi cố gắng chuyên môn hóa tuple_size/ tuple_elementcho một lớp tùy chỉnh trong C ++ 17 để liên kết có cấu trúc.

Mã bên dưới biên dịch trong GCC, nhưng không phải bằng tiếng kêu (cả hai phiên bản trung kế, xem liên kết bên dưới).

#include <type_traits>

template<typename T, typename... Ts>
using sfinae_t = T;

template<typename T, bool... Bs>
using sfinae_v_t = sfinae_t<T, typename std::enable_if<Bs>::type...>;

template <typename T>
struct Test;

template <typename T>
struct Test<sfinae_v_t<T, std::is_integral_v<T>>> {};

void f() {
    Test<int> t;
}

https://godbolt.org/z/ztuRSq

Đây là lỗi được cung cấp bởi clang:

<source>:13:8: error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list

struct Test<sfinae_v_t<T, std::is_integral<T>::value>> {};

       ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1 error generated.

Compiler returned: 1

Đây có phải là một lỗi trong trình biên dịch hoặc mã trên có gọi một số UB không?


3
Điều này có thể được đơn giản hóa hơn nữa .
Evg

3
ICC và MSVC đều không biên dịch được.
ChrisMM

@Evg Thật đáng ngạc nhiên khi gccbiên dịch nó, vì nó không biên dịch được điều này ...
Max Langhof

1
FWIW điều này sẽ được định dạng sai nếu tôi không hoàn toàn nhầm lẫn (vì lý do tương tự rằng điều này là không đúng định dạng).
Max Langhof

1
vì chúng tôi đang trích dẫn tiêu chuẩn, tôi đã thêm thẻ luật sư ngôn ngữ.
Trường đua Guillaume

Câu trả lời:


3

Những gì tôi nói dưới đây (theo OLD POST ) phải đúng ở một mức độ nào đó, nhưng vấn đề thực sự với điều này là, SFINAE được sử dụng sai, do đó tôi không chắc chắn nữa rằng đây là một lỗi trong gcc.

Một khai báo bí danh phải luôn luôn thành công, bạn không thể SFINAE ở đó, vì đó không phải là khai báo lớp hoặc chức năng hoặc chuyên môn hóa (điều đó có nghĩa, vì bạn không thể chuyên biệt các bí danh). Nếu khai báo bí danh không thành công, chương trình không được định dạng. Do đó trình biên dịch có thể giả định rằng nó sẽ không bao giờ đi đến trường hợp khai báo bí danh không thành công cho đến khi bạn buộc nó khởi tạo một mẫu như vậy.

Do đó, trình biên dịch hoàn toàn chấp nhận được khi nghĩ rằng điều đó sfinae_v_t<T,...>luôn luôn Txảy ra, vì điều đó sẽ xảy ra, khi chương trình không được định dạng sai. Do đó, nó sẽ thấy rằng trong tất cả các trường hợp chương trình không được định dạng sai, chuyên môn hóa một phần không chuyên biệt và như vậy nó sẽ cho bạn biết rằng đây là hình thức không đúng. (Đó là những gì clang làm).

Tôi không nghĩ rằng trình biên dịch buộc phải làm điều này. Và nếu nó không, và chỉ nghĩ "Ok, sfinae_v_tlà một loại, bất cứ điều gì.", Thì không rõ ràng đây là một sự khai báo. Vì vậy, tôi nghĩ cho đến khi chúng tôi khởi tạo một trong số chúng thì không có gì sai khi không đưa ra lỗi.

Nhưng khi chúng tôi khởi tạo nó, sẽ có một vấn đề là chúng tôi có một tuyên bố lại hoặc chương trình không được định dạng do std::enable_if, tùy thuộc vào đối số mẫu. GCC nên chọn ít nhất một trong số họ nhưng không.

Điều này cũng hoàn toàn không áp dụng cho ví dụ dễ dàng hơn mà không có std::enable_if. Vì vậy, tôi vẫn nghĩ rằng đây là một lỗi trong GCC, nhưng tôi đủ suy nghĩ rằng tôi không thể nói điều đó với sự chắc chắn nữa. Tôi chỉ muốn nói, ai đó nên báo cáo rằng đó là một lỗi và để những người từ gcc nghĩ về nó.

BÀI ĐĂNG

Đây là một lỗi trong gcc. Tiêu chuẩn cung cấp cho chúng tôi các quy tắc để chuyển đổi một mẫu lớp trong các mẫu hàm. Một mẫu lớp là chuyên biệt hơn so với mẫu khác nếu chức năng của nó đi trước mẫu kia trong thứ tự mẫu hàm một phần.

Tôi đã tạo các hàm ở đây và bây giờ gcc tuyên bố rằng việc gọi chúng là không rõ ràng, do đó cũng phải nói rằng các mẫu lớp được chỉ định như nhau.

Lưu ý: Đọc tiêu chuẩn cẩn thận, trình biên dịch trong đầu tôi đồng ý với tiếng kêu.


Được sfinae_v_t<T, std::is_integral_v<T>>sfinae_v_t<T, !std::is_integral_v<T>>được đối xử như các loại? Về mặt ngữ nghĩa, họ không.
ofo

@GuillaumeRacicot Khá có thể, nhưng tôi muốn hiểu tại sao chính xác. Ví dụ, tiêu chuẩn cũng cho biết "Tên người phụ thuộc không thể được kiểm tra khi khai báo chuyên môn hóa một phần, nhưng sẽ được kiểm tra khi thay thế vào chuyên môn hóa một phần." Điều đó không có nghĩa là liệu chúng có cùng loại được quyết định hay không sau khi thay thế T trong chuyên môn hóa một phần vì sfinae_v_t<T>phụ thuộc vào T? Trong trường hợp đó, chúng sẽ không giống nhau bởi vì một trong hai sẽ bị hình thành xấu.
ofo

@ofo Tôi phải nói rằng, tôi không chắc. Thậm chí có một chút suy nghĩ khi nghĩ về hai thứ đó vì một trong số chúng sẽ không bao giờ là một loại và sử dụng cả hai trong bối cảnh không phải là mẫu sẽ dẫn đến lỗi biên dịch do enable_if_t. Đọc tốt nhất của tôi về tiêu chuẩn là, nó không quan trọng nếu chúng giống nhau hay không. Đối với thứ tự từng phần, chúng ta sẽ luôn so sánh dạng tham số templare của một hàm với dạng đối số khuôn mẫu của hàm kia (nghĩa intlà đã được thay thế) và sau đó có một loại thực sự trong một trong số chúng, vì vậy chúng ta không phải so sánh chúng một cách trừu tượng.
n314159

1
Đào sâu hơn, tôi tìm thấy điều này từ đây . SFINAE nên hoạt động tốt với các bí danh mẫu, nếu không thì template<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;cũng không hoạt động. Tôi sẽ tiếp tục và báo lỗi với gcc, nhưng không thực sự chắc chắn nếu gcc có sai ở đó. Cảm ơn.
từ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.