Chuyên môn hóa dựa trên tính hợp lệ của kích thước mảng


8

Cố gắng chuyên môn hóa dựa trên tính hợp lệ của kích thước mảng:

// base template
template<int p, typename T = void>
struct absolute {
    operator int () const { return 0; }
};

// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
    operator int () const { return p; }
};

// negative case template
template<int p>
struct absolute<p, typename std::void_t<int[-p]>> {
    operator int () const { return -p; }
};


int main() {
    std::cout << absolute<5>() << std::endl;
    std::cout << absolute<-5>() << std::endl;
    std::cout << absolute<0>() << std::endl;
}

Vấn đề # 1:

Mã trên hoạt động độc đáo với gcc nhưng không biên dịch với tiếng kêu .

Clang tạo ra lỗi: xác định lại cấu trúc mẫu 'tuyệt đối'

Ai đúng?


Vấn đề 2:

Cả với gcc và với tiếng kêu (nếu chúng tôi loại bỏ chuyên môn tiêu cực để đưa tiếng kêu trở lại trò chơi), không rõ lý do tại sao absolute<0>()chọn mẫu cơ sở. Không có gì sai với int[0]cũng như std::void_t<int[0]>dường như chuyên sâu hơn:

// base template
template<int p, typename T = void>
struct absolute {
    operator int () const { return -1; }
};

// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
    operator int () const { return p; }
};

int main() {
    std::cout << absolute<5>() << std::endl; // 5
    std::cout << absolute<0>() << std::endl; // -1, why not 0?
}

Và ... nếu mẫu cơ sở chỉ được khai báo mà không thực hiện, như:

// base template
template<int p, typename T = void>
struct absolute;

Cả gcc và clang đều không thể biên dịch , phàn nàn về việc sử dụng loại không đầy đủ cho cuộc gọi : absolute<0>(). Mặc dù nó có vẻ phù hợp với trường hợp chuyên ngành.

Tại sao vậy?


template<int p> struct absolute<p, typename std::void_t<int[p]>>template<int p> struct absoluteđó không phải là một bản sao?
Chipster

3
int[0]Cấm bởi các tiêu chuẩn ISO C ++ chuẩn timsong-cpp.github.io/cppwp/n4659/dcl.array#1 "giá trị của nó sẽ lớn hơn không"
LF

@LF vì vậy không thất bại ở đây: godbolt.org/z/sfSxam là một lỗi với cả clang và gcc?
Amir Kirsh

@AmirKirsh Bạn quên vô hiệu hóa tiện ích mở rộng. godbolt.org/z/RB96uc
LF

Biên dịch trong MSVC là tốt.
eerorika

Câu trả lời:


4

Về lỗi xác định lại của Clang, xem câu hỏi này .

Các id mẫu ban đầu của các mẫu bí danh, chẳng hạn như std::void_tsẽ được thay thế bằng loại bí danh của chúng mà không kiểm tra các đối số cho sự thất bại thay thế. Điều này đã được thay đổi với vấn đề CWG 1558 . Điều này chỉ thay đổi tiêu chuẩn để yêu cầu thất bại thay thế trong các đối số mẫu được thực hiện, nhưng không làm rõ liệu hai mẫu có tương đương sau khi thay thế bí danh có nên được coi là tương đương hay không. Clang coi chúng là tương đương, nhưng GCC thì không. Đây là vấn đề CWG mở năm 1980 .


Với -pedantic-errorsGCC báo cáo một lỗi cứng đã có cho

std::cout << absolute<5>() << std::endl;

trong chuyên ngành

template<int p>
struct absolute<p, typename std::void_t<int[-p]>>

bởi vì được cho là kích thước mảng không phải là biểu thức không đổi. Kích thước của một mảng phải là một biểu thức hằng được chuyển đổi của loại std::size_t. Biểu thức hằng được chuyển đổi chỉ có thể sử dụng các chuyển đổi không thu hẹp. Vì vậy, đúng là -pvới p = 5chuyển đổi thành std::size_tkhông phải là một biểu thức không đổi, làm cho kiểu int[-p]không thành hình, nhưng tôi nghĩ rằng điều đó sẽ gây ra lỗi thay thế, không phải là một lỗi khó. [temp.deduct / 8] của tiêu chuẩn C ++ 17 (dự thảo N4659) nói:

Nếu thay thế dẫn đến một loại hoặc biểu thức không hợp lệ, loại trừ không thành công. Một loại hoặc biểu thức không hợp lệ là một loại sẽ không được định dạng đúng, với yêu cầu chẩn đoán, nếu được viết bằng các đối số được thay thế.

Và điều này áp dụng ở đây. Các ví dụ không quy tắc được đưa ra trong phần trích dẫn thậm chí bao gồm các kích thước mảng âm làm ví dụ cho sự thất bại thay thế.

Điều đặc biệt là đối với absolute<-5>()GCC không báo cáo lỗi tương đương trên

template<int p>
struct absolute<p, typename std::void_t<int[p]>>

chuyên môn hóa, nơi int[p]sẽ đánh giá int[-5]mà cũng không có kích thước biểu thức hằng được chuyển đổi.


absolute<0>()chọn mẫu chính, bởi vì kích thước mảng được yêu cầu phải lớn hơn 0, làm cho các chuyên môn hóa một phần đều không khả thi. Mảng có kích thước bằng không là một phần mở rộng ngôn ngữ có thể bị vô hiệu hóa -pedantic-errorstrong GCC và Clang.


Triển khai phiên bản void_ttác phẩm của riêng chúng tôi cho cả clang và gcc: godbolt.org/z/-2C8mJ để nó có vẻ liên quan đến vấn đề CWG 1558 ... hay nó cũng xử lý vấn đề CWG 1980?
Amir Kirsh

@AmirKirsh Với định nghĩa của bạn, bạn cũng đang tránh được vấn đề CWG 1980. Cả hai đều liên quan đến cách các mẫu bí danh giải quyết các loại không phụ thuộc được xử lý. Định nghĩa của bạn giải quyết make_void<Ts...>::type, đó là một loại phụ thuộc.
quả óc chó

@AmirKirsh Tôi đã không nhận ra rằng GCC chỉ phàn nàn về một kích thước chuyên môn không phải là một biểu thức không đổi. Tôi đã cập nhật câu trả lời để phản ánh điều đó và dường như nó còn rõ ràng hơn với tôi rằng GCC không nhất quán.
quả óc chó
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.